Skip to content

Commit 520ad52

Browse files
author
Robin Cawser
committed
Initial commit
1 parent 1158c71 commit 520ad52

10 files changed

Lines changed: 310 additions & 2 deletions

File tree

Annotation/StringGenerator.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Vivait\StringGeneratorBundle\Annotation;
4+
5+
use Doctrine\Common\Annotations\Annotation;
6+
7+
8+
/**
9+
* @Annotation
10+
*
11+
* Class StringGenerator
12+
* @package Vivait\StringGeneratorBundle\Annotation
13+
*/
14+
class StringGenerator extends Annotation
15+
{
16+
/**
17+
* @var string
18+
*/
19+
public $prefix;
20+
21+
/**
22+
* @var string
23+
*/
24+
public $alphabet = "abcdefjhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345567890";
25+
26+
/**
27+
* @var int
28+
*/
29+
public $length = 8;
30+
31+
/**
32+
* @var string
33+
*/
34+
public $separator = "-";
35+
36+
/**
37+
* @var string
38+
*/
39+
public $prefix_callback;
40+
}
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\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+
23+
24+
// Here you should define the parameters that are allowed to
25+
// configure your bundle. See the documentation linked above for
26+
// more information on that topic.
27+
28+
return $treeBuilder;
29+
}
30+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
26+
$loader->load('services.yml');
27+
}
28+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
9+
class IdGeneratorListener
10+
{
11+
private $reader;
12+
private $repo;
13+
14+
public function __construct(Reader $reader)
15+
{
16+
$this->reader = $reader;
17+
}
18+
19+
/**
20+
* @param LifecycleEventArgs $args
21+
*/
22+
public function prePersist(LifecycleEventArgs $args)
23+
{
24+
$entity = $args->getEntity();
25+
$em = $args->getEntityManager();
26+
$meta = $em->getClassMetadata(get_class($entity));
27+
$this->repo = $em->getRepository($meta->getName());
28+
29+
$obj = new \ReflectionObject($entity);
30+
31+
//Loop through each of entity's properties
32+
foreach ($obj->getProperties() as $property) {
33+
//Loop through the property's annotations
34+
foreach ($this->reader->getPropertyAnnotations($property) as $annotation) {
35+
36+
if ($annotation instanceof Vivait\StringGenerator) {
37+
if (method_exists($entity, $annotation->prefix_callback) && is_callable([$entity, $annotation->prefix_callback])) {
38+
$callback = $annotation->prefix_callback;
39+
$annotation->prefix = $entity->$callback();
40+
}
41+
42+
$id = $this->generateId(
43+
$property->name,
44+
$annotation
45+
);
46+
$meta->getReflectionProperty($property->name)->setValue($entity, $id);
47+
}
48+
}
49+
}
50+
}
51+
52+
/**
53+
* @param $property
54+
* @param $annotation
55+
* @return string
56+
*/
57+
private function generateId($property, Vivait\StringGenerator $annotation)
58+
{
59+
$id = $this->createId($annotation->alphabet, $annotation->length);
60+
61+
if ($annotation->prefix) {
62+
$id = sprintf("%s%s%s", $annotation->prefix, $annotation->separator, $id);
63+
}
64+
65+
if ($this->repo->findOneBy([$property => $id])) {
66+
$this->generateId($property, $annotation);
67+
} else {
68+
return $id;
69+
}
70+
}
71+
72+
/**
73+
* @param $alphabet
74+
* @param $length
75+
* @return string
76+
*/
77+
private function createId($alphabet, $length)
78+
{
79+
$id = [];
80+
$alphaLength = strlen($alphabet) - 1;
81+
for ($i = 0; $i < $length; $i++) {
82+
$n = rand(0, $alphaLength);
83+
$id[] = $alphabet[$n];
84+
}
85+
86+
return implode($id);
87+
}
88+
}

Model/StringGeneratorInterface.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Vivait\StringGeneratorBundle\Model;
4+
5+
interface StringGeneratorInterface {
6+
7+
/**
8+
* Creates a random string
9+
*
10+
* @return string
11+
*/
12+
public static function generate();
13+
}

README.md

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,74 @@
1-
StringGenerateBundle
2-
====================
1+
# StringGeneratorBundle
2+
3+
This bundle allows you to automatically generate a unique random string on an entity property, useful for
4+
creating keys or passwords. 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 quietly
6+
generates a new string. This results as a minimum of one extra query whenever you flush an entity for the first time.
7+
8+
## Basic usage
9+
10+
Add the `@Vivait\StringGenerator()` annotation to an entity property
11+
12+
use Vivait\StringGeneratorBundle\Annotation as Vivait;
13+
14+
/**
15+
* Api
16+
*
17+
* @ORM\Table()
18+
* @ORM\Entity()
19+
*/
20+
class Api
21+
{
22+
/**
23+
* @var integer
24+
*
25+
* @ORM\Column(name="id", type="guid")
26+
* @ORM\Id
27+
* @ORM\GeneratedValue(strategy="UUID")
28+
*/
29+
private $id;
30+
31+
/**
32+
* @var string
33+
*
34+
* @ORM\Column(name="api_id", type="string", nullable=false)
35+
* @Vivait\StringGenerator()
36+
*/
37+
private $api_key;
38+
39+
## Options
40+
41+
### Length
42+
43+
To change the length of the generated string, add `length` to the annotation. The default is 8. Length specifies the length
44+
of the resulting generated string, and does not include the prefix or separator
45+
46+
@Vivait\StringGenerator(length=4)
47+
48+
### Prefix the key
49+
50+
To prefix the string, add the `prefix` option to the annotation. The default seperator between the prefix and generated
51+
string is a `-`, but this can be changed using `separator`:
52+
53+
@Vivait\StringGenerator(prefix="user", separator="_")
54+
55+
A prefix can be obtained via a callback to a method in the entity using `prefix_callback`, which overrides `prefix`.
56+
57+
/**
58+
* @var string
59+
*
60+
* @ORM\Column(name="friendly_id", type="string", nullable=false)
61+
* @Vivait\StringGenerator(prefix_callback="createPrefix", length=8)
62+
*/
63+
private $friendly_id;
64+
65+
public function createPrefix()
66+
{
67+
return $this->category->getCode();
68+
}
69+
70+
### Custom characters
71+
72+
Setting `alphabet` limits the characters the generator can choose from. Defaults to alphanumeric.
73+
74+
@Vivait\StringGenerator(alphabet="abcdefghkmnpqrstuwxyz")

Resources/config/routing.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vivait_friendly_id_homepage:
2+
path: /hello/{name}
3+
defaults: { _controller: VivaitStringGeneratorBundle:Default:index }

Resources/config/services.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
parameters:
2+
3+
services:
4+
vivait_stringgenerator.generator.listener:
5+
class: Vivait\StringGeneratorBundle\EventListener\IdGeneratorListener
6+
arguments: ["@annotation_reader"]
7+
tags:
8+
- { name: doctrine.event_listener, event: prePersist }
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Vivait\StringGeneratorBundle\Tests\Controller;
4+
5+
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
6+
7+
class DefaultControllerTest extends WebTestCase
8+
{
9+
public function testIndex()
10+
{
11+
$client = static::createClient();
12+
13+
$crawler = $client->request('GET', '/hello/Fabien');
14+
15+
$this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0);
16+
}
17+
}

VivaitStringGeneratorBundle.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Vivait\StringGeneratorBundle;
4+
5+
use Symfony\Component\HttpKernel\Bundle\Bundle;
6+
7+
class VivaitStringGeneratorBundle extends Bundle
8+
{
9+
}

0 commit comments

Comments
 (0)