Skip to content

Commit 8daea24

Browse files
author
Robin Cawser
committed
initial commit 1.0
0 parents  commit 8daea24

11 files changed

Lines changed: 429 additions & 0 deletions

File tree

README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# StringGeneratorBundle
2+
3+
This bundle allows you to automatically generate a unique random string on an entity property, useful for
4+
creating keys. Using Doctrine's `prePersist` callback, StringGenerator adds the generated string to a property
5+
before the entity is persisted. It also checks whether the string is unique to that property (just in case) and if not, quietly
6+
generates a new string.
7+
8+
## Install
9+
10+
Add `"vivait/string-generator-bundle": "dev-master"` to your composer.json and run `composer update`
11+
12+
Update your `AppKernel`:
13+
14+
public function registerBundles()
15+
{
16+
$bundles = array(
17+
...
18+
new Vivait\StringGeneratorBundle\VivaitStringGeneratorBundle(),
19+
}
20+
21+
## Basic usage
22+
23+
Add the `@Vivait\StringGenerator()` annotation to an entity property
24+
25+
use Vivait\StringGeneratorBundle\Annotation as Vivait;
26+
27+
/**
28+
* Api
29+
*
30+
* @ORM\Table()
31+
* @ORM\Entity()
32+
*/
33+
class Api
34+
{
35+
/**
36+
* @var integer
37+
*
38+
* @ORM\Column(name="id", type="guid")
39+
* @ORM\Id
40+
* @ORM\GeneratedValue(strategy="UUID")
41+
*/
42+
private $id;
43+
44+
/**
45+
* @var string
46+
*
47+
* @ORM\Column(name="api_id", type="string", nullable=false)
48+
* @Vivait\StringGenerator()
49+
*/
50+
private $api_key;
51+
52+
## Options
53+
54+
### Length
55+
56+
To change the length of the generated string, add `length` to the annotation. The default is 8. Length specifies the length
57+
of the resulting generated string, and does not include the prefix or separator
58+
59+
@Vivait\StringGenerator(length=4)
60+
61+
### Prefix the key
62+
63+
To prefix the string, add the `prefix` option to the annotation. The default seperator between the prefix and generated
64+
string is a `-`, but this can be changed using `separator`:
65+
66+
@Vivait\StringGenerator(prefix="user", separator="_")
67+
68+
A prefix can be obtained via a callback to a method in the entity using `prefix_callback`, which overrides `prefix`.
69+
70+
/**
71+
* @var string
72+
*
73+
* @ORM\Column(name="friendly_id", type="string", nullable=false)
74+
* @Vivait\StringGenerator(prefix_callback="createPrefix", length=8)
75+
*/
76+
private $friendly_id;
77+
78+
public function createPrefix()
79+
{
80+
return $this->category->getCode();
81+
}
82+
83+
### Alphabet
84+
85+
Setting `alphabet` limits the characters the generator can choose from. Defaults to alphanumeric.
86+
87+
@Vivait\StringGenerator(alphabet="abcdefghkmnpqrstuwxyz")
88+
89+
### Unique
90+
91+
Setting `unique` is boolean and tell if the string must be unique or not, by default `true`
92+
93+
@Vivait\StringGenerator(unique="false")
94+
95+
## Custom generator
96+
97+
If you want to use a different generator to create your string, create a class that implements namespace
98+
`Vivait\StringGeneratorBundle\Model\GeneratorInterface`. Add the fully qualified classname to your config.yml:
99+
100+
vivait_string_generator:
101+
generator_class: Acme\BlogBundle\Generator\UsernameGenerator

composer.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name":"vivait/string-generator-bundle",
3+
"description":"Generate random strings for IDs or keys using property annotations",
4+
"authors":[
5+
{
6+
"name":"Robin Cawser",
7+
"email":"robin@vivait.co.uk"
8+
}
9+
],
10+
"minimum-stability": "dev",
11+
"prefer-stable": true,
12+
"require": {
13+
"php": ">=5.3.3",
14+
"doctrine/common": "~2.2"
15+
},
16+
"require-dev": {
17+
"phpspec/phpspec": "~2.0"
18+
},
19+
"config": {
20+
"bin-dir": "bin"
21+
},
22+
"autoload":{
23+
"psr-0":{
24+
"Vivait\\StringGeneratorBundle":"src/"
25+
}
26+
}
27+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace spec\Vivait\StringGeneratorBundle\Generator;
4+
5+
use PhpSpec\ObjectBehavior;
6+
7+
class StringGeneratorSpec extends ObjectBehavior
8+
{
9+
function it_is_initializable()
10+
{
11+
$this->shouldHaveType('Vivait\StringGeneratorBundle\Generator\StrindfggGenerator');
12+
}
13+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Vivait\StringGeneratorBundle\Annotation;
4+
5+
use Doctrine\Common\Annotations\Annotation;
6+
7+
/**
8+
* @Annotation
9+
*
10+
* Class StringGenerator
11+
* @package Vivait\StringGeneratorBundle\Annotation
12+
*/
13+
class StringGenerator extends Annotation
14+
{
15+
/**
16+
* @var string
17+
*/
18+
public $prefix;
19+
20+
/**
21+
* @var int
22+
*/
23+
public $length = 8;
24+
25+
/**
26+
* @var boolean
27+
*/
28+
public $unique = true;
29+
30+
/**
31+
* @var string
32+
*/
33+
public $separator = "-";
34+
35+
/**
36+
* @var string
37+
*/
38+
public $prefix_callback;
39+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Vivait\StringGeneratorBundle\DependencyInjection;
4+
5+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
6+
use Symfony\Component\Config\Definition\ConfigurationInterface;
7+
8+
/**
9+
* This is the class that validates and merges configuration from your app/config files
10+
*
11+
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
12+
*/
13+
class Configuration implements ConfigurationInterface
14+
{
15+
/**
16+
* {@inheritDoc}
17+
*/
18+
public function getConfigTreeBuilder()
19+
{
20+
$treeBuilder = new TreeBuilder();
21+
$rootNode = $treeBuilder->root('vivait_string_generator');
22+
$rootNode
23+
->children()
24+
->scalarNode('generator_class')
25+
->cannotBeEmpty()
26+
->defaultValue('Vivait\StringGeneratorBundle\Generator\StringGenerator')
27+
->end()
28+
->end()
29+
;
30+
31+
return $treeBuilder;
32+
}
33+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Vivait\StringGeneratorBundle\DependencyInjection;
4+
5+
use Symfony\Component\DependencyInjection\ContainerBuilder;
6+
use Symfony\Component\Config\FileLocator;
7+
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
8+
use Symfony\Component\DependencyInjection\Loader;
9+
10+
/**
11+
* This is the class that loads and manages your bundle configuration
12+
*
13+
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
14+
*/
15+
class VivaitStringGeneratorExtension extends Extension
16+
{
17+
/**
18+
* {@inheritDoc}
19+
*/
20+
public function load(array $configs, ContainerBuilder $container)
21+
{
22+
$configuration = new Configuration();
23+
$config = $this->processConfiguration($configuration, $configs);
24+
25+
$container->setParameter('vivait_string_generator.generator_class', $config['generator_class']);
26+
27+
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
28+
$loader->load('services.yml');
29+
}
30+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
namespace Vivait\StringGeneratorBundle\EventListener;
4+
5+
use Doctrine\Common\Annotations\Reader;
6+
use Doctrine\ORM\Event\LifecycleEventArgs;
7+
use Vivait\StringGeneratorBundle\Annotation as Vivait;
8+
use Vivait\StringGeneratorBundle\Model\GeneratorInterface;
9+
10+
class StringGeneratorListener
11+
{
12+
private $reader;
13+
private $repo;
14+
/**
15+
* @var GeneratorInterface
16+
*/
17+
private $generator;
18+
19+
public function __construct(Reader $reader, GeneratorInterface $generator)
20+
{
21+
$this->reader = $reader;
22+
$this->generator = $generator;
23+
}
24+
25+
/**
26+
* @param LifecycleEventArgs $args
27+
*/
28+
public function prePersist(LifecycleEventArgs $args)
29+
{
30+
$entity = $args->getEntity();
31+
$em = $args->getEntityManager();
32+
$meta = $em->getClassMetadata(get_class($entity));
33+
$this->repo = $em->getRepository($meta->getName());
34+
35+
$obj = new \ReflectionObject($entity);
36+
37+
//Loop through each of entity's properties
38+
foreach ($obj->getProperties() as $property) {
39+
//Loop through the property's annotations
40+
foreach ($this->reader->getPropertyAnnotations($property) as $annotation) {
41+
42+
if ($annotation instanceof Vivait\StringGenerator) {
43+
if (method_exists($entity, $annotation->prefix_callback) && is_callable([$entity, $annotation->prefix_callback])) {
44+
$callback = $annotation->prefix_callback;
45+
$annotation->prefix = $entity->$callback();
46+
}
47+
48+
$id = $this->generateId(
49+
$property->name,
50+
$annotation
51+
);
52+
$meta->getReflectionProperty($property->name)->setValue($entity, $id);
53+
}
54+
}
55+
}
56+
}
57+
58+
/**
59+
* @param $property
60+
* @param $annotation
61+
* @return string
62+
*/
63+
private function generateId($property, Vivait\StringGenerator $annotation)
64+
{
65+
$this->generator
66+
->setLength($annotation->length);
67+
68+
$str = $this->generator->generate();
69+
70+
if ($annotation->prefix) {
71+
$str = sprintf("%s%s%s", $annotation->prefix, $annotation->separator, $str);
72+
}
73+
74+
if (!$annotation->unique) {
75+
return $str;
76+
}
77+
78+
if ($this->repo->findOneBy([$property => $str])) {
79+
return $this->generateId($property, $annotation);
80+
} else {
81+
return $str;
82+
}
83+
84+
}
85+
86+
}

0 commit comments

Comments
 (0)