-
-
Notifications
You must be signed in to change notification settings - Fork 0
Snowflake identifier #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
puzzledpolymath
wants to merge
52
commits into
1.x
Choose a base branch
from
snowflake-identifier
base: 1.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
52 commits
Select commit
Hold shift + click to select a range
5c5bf62
Added support for Snowflake identifiers and all variations
puzzledpolymath 09c697e
Updated UUID and ULID keeping psalm satisfied
puzzledpolymath ddb97c1
Added tests for Snowflake identifiers as well as updated tests for UU…
puzzledpolymath 5bc9eb0
Updated documentation
puzzledpolymath eca8ae4
Updated composer dependencies
puzzledpolymath 76ff381
Added classes for handling identifier default values
puzzledpolymath 70758a1
Integrated defaults into identifier classes
puzzledpolymath 289505f
Added tests for identifier defaults
puzzledpolymath f9b8cc4
Updated documentation
puzzledpolymath df00756
Updated documentation
puzzledpolymath 759cd17
Add base Snowflake listener
roxblnfk ed571a2
Refactor SnowflakeDiscord
roxblnfk 5130bbb
Refactor SnowflakeInstagram
roxblnfk bf24d19
Refactor other Snowflakes
roxblnfk e51f831
Refactor UUID listeners
roxblnfk 61ec5ef
style(php-cs-fixer): fix coding standards
fb6df50
Provides fixes after merged changes broke a lot of things
puzzledpolymath 907158d
Provides fixes after merged changes broke a lot of things
puzzledpolymath 259a8ba
Refactor Snowflace typecasters
roxblnfk 376a260
Refactor UUID listeners
roxblnfk ea368f8
Update dependencies
puzzledpolymath e0b77a0
Update docblock description for each Snowflake identifier
puzzledpolymath 70611d2
Updated Ulid following a consistent structure
puzzledpolymath a5e9f10
Updated Ulid following a consistent structure
puzzledpolymath bbbbc77
Adjustments to keep Psalm happy
puzzledpolymath c0e8efd
Adjusted code placement
puzzledpolymath 233fce4
Renamed static factory method and added abstract getTypecast method
puzzledpolymath 8b2d2c4
Implemented UUIDv1 factory and getTypecast
puzzledpolymath 0966faf
Implemented UUIDv2 factory and getTypecast, ensure listener arg local…
puzzledpolymath 46d4d3f
Implemented UUIDv3 factory and getTypecast, made namespace and name o…
puzzledpolymath 8bc4244
Implemented UUIDv4 factory and getTypecast
puzzledpolymath 163bed8
Implemented UUIDv5 factory and getTypecast, made namespace and name o…
puzzledpolymath 605adbf
Implemented UUIDv6 factory and getTypecast
puzzledpolymath 18e62bb
Implemented UUIDv7 factory and getTypecast
puzzledpolymath 7fd75d3
Ensure SnowflakeDiscord getProcessId returns int within range
puzzledpolymath 2aac843
Added UUID factory to base UUID Listener
puzzledpolymath 2d61e62
Updated UUIDv1 Listener to use factory, changed return type to more s…
puzzledpolymath af7cd67
Updated UUIDv2 Listener to use factory, changed return type to more s…
puzzledpolymath e430af8
Updated UUIDv2 Listener to use factory, changed return type to more s…
puzzledpolymath 47d01a5
Updated UUIDv2 Listener to use factory, changed return type to more s…
puzzledpolymath ef86bdd
Updated UUIDv5 Listener to use factory, changed return type to more s…
puzzledpolymath 6240cd8
Updated UUIDv6 Listener to use factory, changed return type to more s…
puzzledpolymath 3c87203
Updated UUIDv7 Listener to use factory, changed return type to more s…
puzzledpolymath 21bc7b7
Additional test fixtures
puzzledpolymath f2c23d8
Updated test cases
puzzledpolymath 2ecded9
Updated documentation
puzzledpolymath 73c830e
style(php-cs-fixer): fix coding standards
cb7c1c3
Removed superfluous PHP docs
puzzledpolymath e54123e
Removed unused imports
puzzledpolymath 1962700
Improved test structure for listener classes
puzzledpolymath 8adf742
Merge remote-tracking branch 'origin/snowflake-identifier' into snowf…
puzzledpolymath 17fc49e
Improved documentation format and content
puzzledpolymath File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Cycle\ORM\Entity\Behavior\Identifier\Listener; | ||
|
|
||
| use Cycle\ORM\Entity\Behavior\Attribute\Listen; | ||
| use Cycle\ORM\Entity\Behavior\Event\Mapper\Command\OnCreate; | ||
| use Ramsey\Identifier\Snowflake\DiscordSnowflakeFactory; | ||
|
|
||
| final class SnowflakeDiscord | ||
| { | ||
| public function __construct( | ||
| private string $field = 'snowflake', | ||
| private int $workerId = 0, | ||
| private int $processId = 0, | ||
| private bool $nullable = false, | ||
| ) {} | ||
|
|
||
| #[Listen(OnCreate::class)] | ||
| public function __invoke(OnCreate $event): void | ||
| { | ||
| if ($this->nullable || isset($event->state->getData()[$this->field])) { | ||
| return; | ||
| } | ||
|
|
||
| $identifier = (new DiscordSnowflakeFactory($this->workerId, $this->processId))->create(); | ||
|
|
||
| $event->state->register($this->field, $identifier); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Cycle\ORM\Entity\Behavior\Identifier\Listener; | ||
|
|
||
| use Cycle\ORM\Entity\Behavior\Attribute\Listen; | ||
| use Cycle\ORM\Entity\Behavior\Event\Mapper\Command\OnCreate; | ||
| use Ramsey\Identifier\Snowflake\Epoch; | ||
| use Ramsey\Identifier\Snowflake\GenericSnowflakeFactory; | ||
|
|
||
| final class SnowflakeGeneric | ||
| { | ||
| public function __construct( | ||
| private string $field = 'snowflake', | ||
| private int $node = 0, | ||
| private Epoch|int $epochOffset = 0, | ||
| private bool $nullable = false, | ||
| ) {} | ||
|
|
||
| #[Listen(OnCreate::class)] | ||
| public function __invoke(OnCreate $event): void | ||
| { | ||
| if ($this->nullable || isset($event->state->getData()[$this->field])) { | ||
| return; | ||
| } | ||
|
|
||
| $identifier = (new GenericSnowflakeFactory($this->node, $this->epochOffset))->create(); | ||
|
|
||
| $event->state->register($this->field, $identifier); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Cycle\ORM\Entity\Behavior\Identifier\Listener; | ||
|
|
||
| use Cycle\ORM\Entity\Behavior\Attribute\Listen; | ||
| use Cycle\ORM\Entity\Behavior\Event\Mapper\Command\OnCreate; | ||
| use Ramsey\Identifier\Snowflake\InstagramSnowflakeFactory; | ||
|
|
||
| final class SnowflakeInstagram | ||
| { | ||
| public function __construct( | ||
| private string $field = 'snowflake', | ||
| private int $shardId = 0, | ||
| private bool $nullable = false, | ||
| ) {} | ||
|
|
||
| #[Listen(OnCreate::class)] | ||
| public function __invoke(OnCreate $event): void | ||
| { | ||
| if ($this->nullable || isset($event->state->getData()[$this->field])) { | ||
| return; | ||
| } | ||
|
|
||
| $identifier = (new InstagramSnowflakeFactory($this->shardId))->create(); | ||
|
|
||
| $event->state->register($this->field, $identifier); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Cycle\ORM\Entity\Behavior\Identifier\Listener; | ||
|
|
||
| use Cycle\ORM\Entity\Behavior\Attribute\Listen; | ||
| use Cycle\ORM\Entity\Behavior\Event\Mapper\Command\OnCreate; | ||
| use Ramsey\Identifier\Snowflake\MastodonSnowflakeFactory; | ||
|
|
||
| final class SnowflakeMastodon | ||
| { | ||
| /** | ||
| * @param non-empty-string|null $tableName | ||
| */ | ||
| public function __construct( | ||
| private string $field = 'snowflake', | ||
| private ?string $tableName = null, | ||
| private bool $nullable = false, | ||
| ) {} | ||
|
|
||
| #[Listen(OnCreate::class)] | ||
| public function __invoke(OnCreate $event): void | ||
| { | ||
| if ($this->nullable || isset($event->state->getData()[$this->field])) { | ||
| return; | ||
| } | ||
|
|
||
| $identifier = (new MastodonSnowflakeFactory($this->tableName))->create(); | ||
|
|
||
| $event->state->register($this->field, $identifier); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Cycle\ORM\Entity\Behavior\Identifier\Listener; | ||
|
|
||
| use Cycle\ORM\Entity\Behavior\Attribute\Listen; | ||
| use Cycle\ORM\Entity\Behavior\Event\Mapper\Command\OnCreate; | ||
| use Ramsey\Identifier\Snowflake\TwitterSnowflakeFactory; | ||
|
|
||
| final class SnowflakeTwitter | ||
| { | ||
| public function __construct( | ||
| private string $field = 'snowflake', | ||
| private int $machineId = 0, | ||
| private bool $nullable = false, | ||
| ) {} | ||
|
|
||
| #[Listen(OnCreate::class)] | ||
| public function __invoke(OnCreate $event): void | ||
| { | ||
| if ($this->nullable || isset($event->state->getData()[$this->field])) { | ||
| return; | ||
| } | ||
|
|
||
| $identifier = (new TwitterSnowflakeFactory($this->machineId))->create(); | ||
|
|
||
| $event->state->register($this->field, $identifier); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Cycle\ORM\Entity\Behavior\Identifier; | ||
|
|
||
| use Cycle\ORM\Entity\Behavior\Schema\BaseModifier; | ||
| use Cycle\ORM\Entity\Behavior\Schema\RegistryModifier; | ||
| use Cycle\ORM\Schema\GeneratedField; | ||
| use Cycle\Schema\Registry; | ||
| use Ramsey\Identifier\SnowflakeFactory; | ||
|
|
||
| abstract class Snowflake extends BaseModifier | ||
| { | ||
| protected ?string $column = null; | ||
| protected string $field; | ||
| protected bool $nullable = false; | ||
|
|
||
| #[\Override] | ||
| public function compute(Registry $registry): void | ||
| { | ||
| $modifier = new RegistryModifier($registry, $this->role); | ||
| $this->column = $modifier->findColumnName($this->field, $this->column); | ||
| if (\is_string($this->column) && $this->column !== '') { | ||
| $modifier->addSnowflakeColumn( | ||
| $this->column, | ||
| $this->field, | ||
| $this->nullable ? null : GeneratedField::BEFORE_INSERT, | ||
| )->nullable($this->nullable); | ||
|
|
||
| $factory = $this->snowflakeFactory(); | ||
|
|
||
| $modifier->setTypecast( | ||
| $registry->getEntity($this->role)->getFields()->get($this->field), | ||
| [$factory, 'createFromInteger'], | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| #[\Override] | ||
| public function render(Registry $registry): void | ||
| { | ||
| $modifier = new RegistryModifier($registry, $this->role); | ||
| /** @var non-empty-string column */ | ||
| $this->column = $modifier->findColumnName($this->field, $this->column) ?? $this->field; | ||
|
|
||
| $modifier->addSnowflakeColumn( | ||
| $this->column, | ||
| $this->field, | ||
| $this->nullable ? null : GeneratedField::BEFORE_INSERT, | ||
| )->nullable($this->nullable); | ||
|
|
||
| $factory = $this->snowflakeFactory(); | ||
|
|
||
| $modifier->setTypecast( | ||
| $registry->getEntity($this->role)->getFields()->get($this->field), | ||
| [$factory, 'createFromInteger'], | ||
| ); | ||
| } | ||
|
|
||
| abstract protected function snowflakeFactory(): SnowflakeFactory; | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ORM schema should be serializable. That's why a static factory should be used here. Is it possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the ramsey/identifier package has taken a different direction and moved away from static classes/factories. Not a bad thing, but it likely means we’ll need to create our own static classes/methods for instantiating these factories 🥲
While I wasn’t aware of the serialisation requirement or that it was an issue, the way I originally implemented this seemed the most logical at the time, given some factories require constructor arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry not at my desk at moment, it’s 3:20am here in Australia. But I’ll throw out any idea.
An option might be: define a static method within each behaviour class. E.g
public static function fromInteger(array $args);Then there’s the issue of needing to supply the ramsey factories with behaviour values into its constructor. Perhaps we could support a third value in the array?
[self::class, 'fromInteger', [1, 2]Or allow the callback method to be defined similar to how enum annotations are declared.
[self::class, 'fromInteger(1, 2)']Obviously this would require some passing, and changes so that the supplied parameters are captured and provided back to the static method when invoked.
Would something like that work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's interesting why there is any state if we want to load already generated IDs. We won't know the state of generators for those IDs that are in the database in advance.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not seeing an issue when generating a schema. Or am I am misunderstanding the problem?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the schema, there should be no function calls, especially unserialize. It should be an array that can be packed into a text cache format, like JSON.
Ideally, we need static factories.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for clarifying.
Image below depicts some changes to
Cycle\ORM\Parser\Typecastwhich I think might aid in working around this problem with serialization.Then within the base classes, instead of something like:
You might do:
Where the
fromIntegermethod looks something like.All theory and completely untested. Just wanted to run the idea by you first before taking it any further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI ramsey/identifier#38