Skip to content

Latest commit

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

valueobjects

Build StatusVersion

Requirements

Requires PHP >= 7.1

Installation

Through Composer, obviously:

composer require funeralzone/valueobjects

Extensions

This library only deals with fundamental values (scalars). We've also released an extension library which provides a starting point for more complex values.

Our approach

We've written up our philosophy to PHP value objects in our A better way of writing value objects in PHP article.

Single value object

If your VO encapsulates a single value, it's most likely a scalar. We've provided some traits to deal with scalars under:

src/Scalars

Let's say you have a domain value called 'User Email'. You'd create a class which implements the ValueObject interface:

finalclass UserEmail implements ValueObject {
...

You now need to implement the interface. But because an email can essentially be considered a special kind of string (in this simple case) the StringTrait helper trait can implement most of the interface for you:

finalclass UserEmail implements ValueObject {
use StringTrait;
...

In our case, a user's email has other domain logic that we can encapsulate in our VO. User emails have to be a valid email:

...
public function__construct(string $string)
{
Assert::that($string)->email();
$this->string = $string;
}
...

You can see an example of how to implement single value objects in the examples directory.

Enums

Enums can be defined easily through use of the EnumTrait. Then, the enum values are simply listed as constants on the class.

finalclass Fastening implements ValueObject
{
use EnumTrait;
publicconstBUTTON = 0;
publicconstCLIP = 1;
publicconstPIN = 2;
publicconstZIP = 3;
}

When dealing with value object serialisation, the constant names are used. They are case-sensitive. So:

$fastening = Fastening::fromNative('BUTTON');
$fastening->toNative(); // Equals to string: 'BUTTON'

In code, the trait utilises magic methods to create objects based on constant name like so:

$fastening = Fastening::ZIP();
$fastening->toNative(); // Equals 'ZIP'

If your IDE supports code completion and you'd like to use named methods to create enums you can add the following PHPDoc block to your enum class:

/** * @method static Fastening BUTTON() * @method static Fastening CLIP() * @method static Fastening PIN() * @method static Fastening ZIP() */finalclass Fastening implements ValueObject

Composite value objects

A composite value object is a more complex value which is made from other values.

finalclass Location implements ValueObject
{
use CompositeTrait;
private$latitude;
private$longitude;
publicfunction__construct(Latitude$latitude, Longitude$longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
}
publicfunctiongetLatitude(): Latitude
{
return$this->latitude;
}
publicfunctiongetLongitude(): Longitude
{
return$this->longitude;
}
...

A Location is made up of two VOs (latitude, longitude). We've provided a CompositeTrait to easily implement most of the ValueObject interface automatically. It handles toNative serialistation by using reflection to return an array of all the class properties.

The CompositeTrait does not implement fromNative. We leave the construction of your object up to you.

...
public staticfunctionfromNative($native)
{
returnnewstatic(
Latitude::fromNative($native['latitude']),
Longitude::fromNative($native['longitude'])
);
}
...

You can see an example of how to implement composite objects in the examples directory.

Nulls, NonNulls and Nullables

This package allows you to deal with nullable value objects.

First create a type of value object.

interface PhoneNumber extends ValueObject
{
}

Implement a non-null version of the value object.

finalclass NonNullPhoneNumber implements PhoneNumber
{
use StringTrait;
}

Implement a null version of the value object.

finalclass NullPhoneNumber implements PhoneNumber
{
use NullTrait;
}

Implement a nullable version of the value object.

finalclass NullablePhoneNumber extends Nullable implements PhoneNumber
{
protectedstaticfunctionnonNullImplementation(): string
{
return NonNullPhoneNumber::class;
}
protectedstaticfunctionnullImplementation(): string
{
return NullPhoneNumber::class;
}
}

This 'nullable' handles automatic creation of either a null or a non-null version of the interface based on the native input. For example:

$phoneNumber = NullablePhoneNumber::fromNative(null);

The $phoneNumber above will automatically use the NullPhoneNumber implementation specified above.

Or:

$phoneNumber = NullablePhoneNumber::fromNative('+44 73715525763');

The $phoneNumber above will automatically use the NonNullPhoneNumber implementation specified above.

Sets of value objects

A set of value objects should implement the Set interface. It's just an extension of the ValueObject interface with a few simple additions.

interface Set extends ValueObject, \IteratorAggregate, \ArrayAccess, \Countable
{
publicfunctionadd($set);
publicfunctionremove($set);
publicfunctioncontains(ValueObject$value): bool;
publicfunctiontoArray(): array;
}
  • add Add values from another set to the current set.
  • remove Remove all the values contained in another set from the current set.
  • contains Returns true if the value exists in the current set.
  • toArray Returns a simple PHP array containing all of the value objects.

The other interfaces that the Set interface extends from (\IteratorAggregate, \ArrayAccess, \Countable) are for accessing the set object as though it was an array.

Non-null sets

The library provides a default implementation of the interface.

finalclass SetOfLocations extends NonNullSet implements Set
{
protectedfunctiontypeToEnforce(): string
{
return Location::class;
}
publicstaticfunctionvaluesShouldBeUnique(): bool
{
returntrue;
}
}

There are two abstract methods that need to be implemented.

  • typeToEnforce should return a string of the class name of the value object that you want to make a set of.
  • valuesShouldBeUnique should return a boolean representing whether you want to force the set to be unique.

If the set is set to unique, if duplicate values are added to the set (at instantiation or through the add method) the duplicates are filtered out.

Null and nullable sets

Just like standard value objects there are some constructs to help with creating nullable and null sets. See the Nulls, NonNulls and Nullables section for more information.

  • NullableSet The set equivalent of Nullable.
  • NullSetTrait The set equivalent of the NullTrait.

Usage of sets

Iteration, access and counting

// Iteration$set = newSetOfLocations([$one, $two]);
foreach($setas$value) {
// TODO: Do something with each value object
}
// Access$one = $set[0];
$two = $set[1];
//Counting$count = count($set); // Returns 2

add

Merges another set.

$set = newSetOfLocations([$one, $two]);
$anotherSet = newSetOfLocations([$three]);
$mergedSet = $set->add($anotherSet);
count($mergedSet) // Equals: 3

remove

Removes values from a set by using another set as reference values.

$set = newSetOfLocations([$one, $two, $three]);
$anotherSet = newSetOfLocations([$one]);
$remove = $set->remove($anotherSet);
count($remove) // Equals: 2

contains

Checks whether a set contains a particular value object.

$set = newSetOfLocations([$one, $two, $three]);
$one = newLocation(0);
$check = $set->contains($one);

About

A PHP 7 value objects helper library.

Resources

Stars

66 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - funeralzone/valueobjects: A PHP 7 value objects helper library. · GitHub
Skip to content

Latest commit

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

valueobjects

Build StatusVersion

Requirements

Requires PHP >= 7.1

Installation

Through Composer, obviously:

composer require funeralzone/valueobjects

Extensions

This library only deals with fundamental values (scalars). We've also released an extension library which provides a starting point for more complex values.

Our approach

We've written up our philosophy to PHP value objects in our A better way of writing value objects in PHP article.

Single value object

If your VO encapsulates a single value, it's most likely a scalar. We've provided some traits to deal with scalars under:

src/Scalars

Let's say you have a domain value called 'User Email'. You'd create a class which implements the ValueObject interface:

finalclass UserEmail implements ValueObject {
...

You now need to implement the interface. But because an email can essentially be considered a special kind of string (in this simple case) the StringTrait helper trait can implement most of the interface for you:

finalclass UserEmail implements ValueObject {
use StringTrait;
...

In our case, a user's email has other domain logic that we can encapsulate in our VO. User emails have to be a valid email:

...
public function__construct(string $string)
{
Assert::that($string)->email();
$this->string = $string;
}
...

You can see an example of how to implement single value objects in the examples directory.

Enums

Enums can be defined easily through use of the EnumTrait. Then, the enum values are simply listed as constants on the class.

finalclass Fastening implements ValueObject
{
use EnumTrait;
publicconstBUTTON = 0;
publicconstCLIP = 1;
publicconstPIN = 2;
publicconstZIP = 3;
}

When dealing with value object serialisation, the constant names are used. They are case-sensitive. So:

$fastening = Fastening::fromNative('BUTTON');
$fastening->toNative(); // Equals to string: 'BUTTON'

In code, the trait utilises magic methods to create objects based on constant name like so:

$fastening = Fastening::ZIP();
$fastening->toNative(); // Equals 'ZIP'

If your IDE supports code completion and you'd like to use named methods to create enums you can add the following PHPDoc block to your enum class:

/** * @method static Fastening BUTTON() * @method static Fastening CLIP() * @method static Fastening PIN() * @method static Fastening ZIP() */finalclass Fastening implements ValueObject

Composite value objects

A composite value object is a more complex value which is made from other values.

finalclass Location implements ValueObject
{
use CompositeTrait;
private$latitude;
private$longitude;
publicfunction__construct(Latitude$latitude, Longitude$longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
}
publicfunctiongetLatitude(): Latitude
{
return$this->latitude;
}
publicfunctiongetLongitude(): Longitude
{
return$this->longitude;
}
...

A Location is made up of two VOs (latitude, longitude). We've provided a CompositeTrait to easily implement most of the ValueObject interface automatically. It handles toNative serialistation by using reflection to return an array of all the class properties.

The CompositeTrait does not implement fromNative. We leave the construction of your object up to you.

...
public staticfunctionfromNative($native)
{
returnnewstatic(
Latitude::fromNative($native['latitude']),
Longitude::fromNative($native['longitude'])
);
}
...

You can see an example of how to implement composite objects in the examples directory.

Nulls, NonNulls and Nullables

This package allows you to deal with nullable value objects.

First create a type of value object.

interface PhoneNumber extends ValueObject
{
}

Implement a non-null version of the value object.

finalclass NonNullPhoneNumber implements PhoneNumber
{
use StringTrait;
}

Implement a null version of the value object.

finalclass NullPhoneNumber implements PhoneNumber
{
use NullTrait;
}

Implement a nullable version of the value object.

finalclass NullablePhoneNumber extends Nullable implements PhoneNumber
{
protectedstaticfunctionnonNullImplementation(): string
{
return NonNullPhoneNumber::class;
}
protectedstaticfunctionnullImplementation(): string
{
return NullPhoneNumber::class;
}
}

This 'nullable' handles automatic creation of either a null or a non-null version of the interface based on the native input. For example:

$phoneNumber = NullablePhoneNumber::fromNative(null);

The $phoneNumber above will automatically use the NullPhoneNumber implementation specified above.

Or:

$phoneNumber = NullablePhoneNumber::fromNative('+44 73715525763');

The $phoneNumber above will automatically use the NonNullPhoneNumber implementation specified above.

Sets of value objects

A set of value objects should implement the Set interface. It's just an extension of the ValueObject interface with a few simple additions.

interface Set extends ValueObject, \IteratorAggregate, \ArrayAccess, \Countable
{
publicfunctionadd($set);
publicfunctionremove($set);
publicfunctioncontains(ValueObject$value): bool;
publicfunctiontoArray(): array;
}
  • add Add values from another set to the current set.
  • remove Remove all the values contained in another set from the current set.
  • contains Returns true if the value exists in the current set.
  • toArray Returns a simple PHP array containing all of the value objects.

The other interfaces that the Set interface extends from (\IteratorAggregate, \ArrayAccess, \Countable) are for accessing the set object as though it was an array.

Non-null sets

The library provides a default implementation of the interface.

finalclass SetOfLocations extends NonNullSet implements Set
{
protectedfunctiontypeToEnforce(): string
{
return Location::class;
}
publicstaticfunctionvaluesShouldBeUnique(): bool
{
returntrue;
}
}

There are two abstract methods that need to be implemented.

  • typeToEnforce should return a string of the class name of the value object that you want to make a set of.
  • valuesShouldBeUnique should return a boolean representing whether you want to force the set to be unique.

If the set is set to unique, if duplicate values are added to the set (at instantiation or through the add method) the duplicates are filtered out.

Null and nullable sets

Just like standard value objects there are some constructs to help with creating nullable and null sets. See the Nulls, NonNulls and Nullables section for more information.

  • NullableSet The set equivalent of Nullable.
  • NullSetTrait The set equivalent of the NullTrait.

Usage of sets

Iteration, access and counting

// Iteration$set = newSetOfLocations([$one, $two]);
foreach($setas$value) {
// TODO: Do something with each value object
}
// Access$one = $set[0];
$two = $set[1];
//Counting$count = count($set); // Returns 2

add

Merges another set.

$set = newSetOfLocations([$one, $two]);
$anotherSet = newSetOfLocations([$three]);
$mergedSet = $set->add($anotherSet);
count($mergedSet) // Equals: 3

remove

Removes values from a set by using another set as reference values.

$set = newSetOfLocations([$one, $two, $three]);
$anotherSet = newSetOfLocations([$one]);
$remove = $set->remove($anotherSet);
count($remove) // Equals: 2

contains

Checks whether a set contains a particular value object.

$set = newSetOfLocations([$one, $two, $three]);
$one = newLocation(0);
$check = $set->contains($one);

About

A PHP 7 value objects helper library.

Resources

Stars

66 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - funeralzone/valueobjects: A PHP 7 value objects helper library. · GitHub
Skip to content

Latest commit

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

valueobjects

Build StatusVersion

Requirements

Requires PHP >= 7.1

Installation

Through Composer, obviously:

composer require funeralzone/valueobjects

Extensions

This library only deals with fundamental values (scalars). We've also released an extension library which provides a starting point for more complex values.

Our approach

We've written up our philosophy to PHP value objects in our A better way of writing value objects in PHP article.

Single value object

If your VO encapsulates a single value, it's most likely a scalar. We've provided some traits to deal with scalars under:

src/Scalars

Let's say you have a domain value called 'User Email'. You'd create a class which implements the ValueObject interface:

finalclass UserEmail implements ValueObject {
...

You now need to implement the interface. But because an email can essentially be considered a special kind of string (in this simple case) the StringTrait helper trait can implement most of the interface for you:

finalclass UserEmail implements ValueObject {
use StringTrait;
...

In our case, a user's email has other domain logic that we can encapsulate in our VO. User emails have to be a valid email:

...
public function__construct(string $string)
{
Assert::that($string)->email();
$this->string = $string;
}
...

You can see an example of how to implement single value objects in the examples directory.

Enums

Enums can be defined easily through use of the EnumTrait. Then, the enum values are simply listed as constants on the class.

finalclass Fastening implements ValueObject
{
use EnumTrait;
publicconstBUTTON = 0;
publicconstCLIP = 1;
publicconstPIN = 2;
publicconstZIP = 3;
}

When dealing with value object serialisation, the constant names are used. They are case-sensitive. So:

$fastening = Fastening::fromNative('BUTTON');
$fastening->toNative(); // Equals to string: 'BUTTON'

In code, the trait utilises magic methods to create objects based on constant name like so:

$fastening = Fastening::ZIP();
$fastening->toNative(); // Equals 'ZIP'

If your IDE supports code completion and you'd like to use named methods to create enums you can add the following PHPDoc block to your enum class:

/** * @method static Fastening BUTTON() * @method static Fastening CLIP() * @method static Fastening PIN() * @method static Fastening ZIP() */finalclass Fastening implements ValueObject

Composite value objects

A composite value object is a more complex value which is made from other values.

finalclass Location implements ValueObject
{
use CompositeTrait;
private$latitude;
private$longitude;
publicfunction__construct(Latitude$latitude, Longitude$longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
}
publicfunctiongetLatitude(): Latitude
{
return$this->latitude;
}
publicfunctiongetLongitude(): Longitude
{
return$this->longitude;
}
...

A Location is made up of two VOs (latitude, longitude). We've provided a CompositeTrait to easily implement most of the ValueObject interface automatically. It handles toNative serialistation by using reflection to return an array of all the class properties.

The CompositeTrait does not implement fromNative. We leave the construction of your object up to you.

...
public staticfunctionfromNative($native)
{
returnnewstatic(
Latitude::fromNative($native['latitude']),
Longitude::fromNative($native['longitude'])
);
}
...

You can see an example of how to implement composite objects in the examples directory.

Nulls, NonNulls and Nullables

This package allows you to deal with nullable value objects.

First create a type of value object.

interface PhoneNumber extends ValueObject
{
}

Implement a non-null version of the value object.

finalclass NonNullPhoneNumber implements PhoneNumber
{
use StringTrait;
}

Implement a null version of the value object.

finalclass NullPhoneNumber implements PhoneNumber
{
use NullTrait;
}

Implement a nullable version of the value object.

finalclass NullablePhoneNumber extends Nullable implements PhoneNumber
{
protectedstaticfunctionnonNullImplementation(): string
{
return NonNullPhoneNumber::class;
}
protectedstaticfunctionnullImplementation(): string
{
return NullPhoneNumber::class;
}
}

This 'nullable' handles automatic creation of either a null or a non-null version of the interface based on the native input. For example:

$phoneNumber = NullablePhoneNumber::fromNative(null);

The $phoneNumber above will automatically use the NullPhoneNumber implementation specified above.

Or:

$phoneNumber = NullablePhoneNumber::fromNative('+44 73715525763');

The $phoneNumber above will automatically use the NonNullPhoneNumber implementation specified above.

Sets of value objects

A set of value objects should implement the Set interface. It's just an extension of the ValueObject interface with a few simple additions.

interface Set extends ValueObject, \IteratorAggregate, \ArrayAccess, \Countable
{
publicfunctionadd($set);
publicfunctionremove($set);
publicfunctioncontains(ValueObject$value): bool;
publicfunctiontoArray(): array;
}
  • add Add values from another set to the current set.
  • remove Remove all the values contained in another set from the current set.
  • contains Returns true if the value exists in the current set.
  • toArray Returns a simple PHP array containing all of the value objects.

The other interfaces that the Set interface extends from (\IteratorAggregate, \ArrayAccess, \Countable) are for accessing the set object as though it was an array.

Non-null sets

The library provides a default implementation of the interface.

finalclass SetOfLocations extends NonNullSet implements Set
{
protectedfunctiontypeToEnforce(): string
{
return Location::class;
}
publicstaticfunctionvaluesShouldBeUnique(): bool
{
returntrue;
}
}

There are two abstract methods that need to be implemented.

  • typeToEnforce should return a string of the class name of the value object that you want to make a set of.
  • valuesShouldBeUnique should return a boolean representing whether you want to force the set to be unique.

If the set is set to unique, if duplicate values are added to the set (at instantiation or through the add method) the duplicates are filtered out.

Null and nullable sets

Just like standard value objects there are some constructs to help with creating nullable and null sets. See the Nulls, NonNulls and Nullables section for more information.

  • NullableSet The set equivalent of Nullable.
  • NullSetTrait The set equivalent of the NullTrait.

Usage of sets

Iteration, access and counting

// Iteration$set = newSetOfLocations([$one, $two]);
foreach($setas$value) {
// TODO: Do something with each value object
}
// Access$one = $set[0];
$two = $set[1];
//Counting$count = count($set); // Returns 2

add

Merges another set.

$set = newSetOfLocations([$one, $two]);
$anotherSet = newSetOfLocations([$three]);
$mergedSet = $set->add($anotherSet);
count($mergedSet) // Equals: 3

remove

Removes values from a set by using another set as reference values.

$set = newSetOfLocations([$one, $two, $three]);
$anotherSet = newSetOfLocations([$one]);
$remove = $set->remove($anotherSet);
count($remove) // Equals: 2

contains

Checks whether a set contains a particular value object.

$set = newSetOfLocations([$one, $two, $three]);
$one = newLocation(0);
$check = $set->contains($one);

About

A PHP 7 value objects helper library.

Resources

Stars

66 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - funeralzone/valueobjects: A PHP 7 value objects helper library. · GitHub
Skip to content

Latest commit

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

valueobjects

Build StatusVersion

Requirements

Requires PHP >= 7.1

Installation

Through Composer, obviously:

composer require funeralzone/valueobjects

Extensions

This library only deals with fundamental values (scalars). We've also released an extension library which provides a starting point for more complex values.

Our approach

We've written up our philosophy to PHP value objects in our A better way of writing value objects in PHP article.

Single value object

If your VO encapsulates a single value, it's most likely a scalar. We've provided some traits to deal with scalars under:

src/Scalars

Let's say you have a domain value called 'User Email'. You'd create a class which implements the ValueObject interface:

finalclass UserEmail implements ValueObject {
...

You now need to implement the interface. But because an email can essentially be considered a special kind of string (in this simple case) the StringTrait helper trait can implement most of the interface for you:

finalclass UserEmail implements ValueObject {
use StringTrait;
...

In our case, a user's email has other domain logic that we can encapsulate in our VO. User emails have to be a valid email:

...
public function__construct(string $string)
{
Assert::that($string)->email();
$this->string = $string;
}
...

You can see an example of how to implement single value objects in the examples directory.

Enums

Enums can be defined easily through use of the EnumTrait. Then, the enum values are simply listed as constants on the class.

finalclass Fastening implements ValueObject
{
use EnumTrait;
publicconstBUTTON = 0;
publicconstCLIP = 1;
publicconstPIN = 2;
publicconstZIP = 3;
}

When dealing with value object serialisation, the constant names are used. They are case-sensitive. So:

$fastening = Fastening::fromNative('BUTTON');
$fastening->toNative(); // Equals to string: 'BUTTON'

In code, the trait utilises magic methods to create objects based on constant name like so:

$fastening = Fastening::ZIP();
$fastening->toNative(); // Equals 'ZIP'

If your IDE supports code completion and you'd like to use named methods to create enums you can add the following PHPDoc block to your enum class:

/** * @method static Fastening BUTTON() * @method static Fastening CLIP() * @method static Fastening PIN() * @method static Fastening ZIP() */finalclass Fastening implements ValueObject

Composite value objects

A composite value object is a more complex value which is made from other values.

finalclass Location implements ValueObject
{
use CompositeTrait;
private$latitude;
private$longitude;
publicfunction__construct(Latitude$latitude, Longitude$longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
}
publicfunctiongetLatitude(): Latitude
{
return$this->latitude;
}
publicfunctiongetLongitude(): Longitude
{
return$this->longitude;
}
...

A Location is made up of two VOs (latitude, longitude). We've provided a CompositeTrait to easily implement most of the ValueObject interface automatically. It handles toNative serialistation by using reflection to return an array of all the class properties.

The CompositeTrait does not implement fromNative. We leave the construction of your object up to you.

...
public staticfunctionfromNative($native)
{
returnnewstatic(
Latitude::fromNative($native['latitude']),
Longitude::fromNative($native['longitude'])
);
}
...

You can see an example of how to implement composite objects in the examples directory.

Nulls, NonNulls and Nullables

This package allows you to deal with nullable value objects.

First create a type of value object.

interface PhoneNumber extends ValueObject
{
}

Implement a non-null version of the value object.

finalclass NonNullPhoneNumber implements PhoneNumber
{
use StringTrait;
}

Implement a null version of the value object.

finalclass NullPhoneNumber implements PhoneNumber
{
use NullTrait;
}

Implement a nullable version of the value object.

finalclass NullablePhoneNumber extends Nullable implements PhoneNumber
{
protectedstaticfunctionnonNullImplementation(): string
{
return NonNullPhoneNumber::class;
}
protectedstaticfunctionnullImplementation(): string
{
return NullPhoneNumber::class;
}
}

This 'nullable' handles automatic creation of either a null or a non-null version of the interface based on the native input. For example:

$phoneNumber = NullablePhoneNumber::fromNative(null);

The $phoneNumber above will automatically use the NullPhoneNumber implementation specified above.

Or:

$phoneNumber = NullablePhoneNumber::fromNative('+44 73715525763');

The $phoneNumber above will automatically use the NonNullPhoneNumber implementation specified above.

Sets of value objects

A set of value objects should implement the Set interface. It's just an extension of the ValueObject interface with a few simple additions.

interface Set extends ValueObject, \IteratorAggregate, \ArrayAccess, \Countable
{
publicfunctionadd($set);
publicfunctionremove($set);
publicfunctioncontains(ValueObject$value): bool;
publicfunctiontoArray(): array;
}
  • add Add values from another set to the current set.
  • remove Remove all the values contained in another set from the current set.
  • contains Returns true if the value exists in the current set.
  • toArray Returns a simple PHP array containing all of the value objects.

The other interfaces that the Set interface extends from (\IteratorAggregate, \ArrayAccess, \Countable) are for accessing the set object as though it was an array.

Non-null sets

The library provides a default implementation of the interface.

finalclass SetOfLocations extends NonNullSet implements Set
{
protectedfunctiontypeToEnforce(): string
{
return Location::class;
}
publicstaticfunctionvaluesShouldBeUnique(): bool
{
returntrue;
}
}

There are two abstract methods that need to be implemented.

  • typeToEnforce should return a string of the class name of the value object that you want to make a set of.
  • valuesShouldBeUnique should return a boolean representing whether you want to force the set to be unique.

If the set is set to unique, if duplicate values are added to the set (at instantiation or through the add method) the duplicates are filtered out.

Null and nullable sets

Just like standard value objects there are some constructs to help with creating nullable and null sets. See the Nulls, NonNulls and Nullables section for more information.

  • NullableSet The set equivalent of Nullable.
  • NullSetTrait The set equivalent of the NullTrait.

Usage of sets

Iteration, access and counting

// Iteration$set = newSetOfLocations([$one, $two]);
foreach($setas$value) {
// TODO: Do something with each value object
}
// Access$one = $set[0];
$two = $set[1];
//Counting$count = count($set); // Returns 2

add

Merges another set.

$set = newSetOfLocations([$one, $two]);
$anotherSet = newSetOfLocations([$three]);
$mergedSet = $set->add($anotherSet);
count($mergedSet) // Equals: 3

remove

Removes values from a set by using another set as reference values.

$set = newSetOfLocations([$one, $two, $three]);
$anotherSet = newSetOfLocations([$one]);
$remove = $set->remove($anotherSet);
count($remove) // Equals: 2

contains

Checks whether a set contains a particular value object.

$set = newSetOfLocations([$one, $two, $three]);
$one = newLocation(0);
$check = $set->contains($one);

About

A PHP 7 value objects helper library.

Resources

Stars

66 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - funeralzone/valueobjects: A PHP 7 value objects helper library. · GitHub
Skip to content

Latest commit

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

valueobjects

Build StatusVersion

Requirements

Requires PHP >= 7.1

Installation

Through Composer, obviously:

composer require funeralzone/valueobjects

Extensions

This library only deals with fundamental values (scalars). We've also released an extension library which provides a starting point for more complex values.

Our approach

We've written up our philosophy to PHP value objects in our A better way of writing value objects in PHP article.

Single value object

If your VO encapsulates a single value, it's most likely a scalar. We've provided some traits to deal with scalars under:

src/Scalars

Let's say you have a domain value called 'User Email'. You'd create a class which implements the ValueObject interface:

finalclass UserEmail implements ValueObject {
...

You now need to implement the interface. But because an email can essentially be considered a special kind of string (in this simple case) the StringTrait helper trait can implement most of the interface for you:

finalclass UserEmail implements ValueObject {
use StringTrait;
...

In our case, a user's email has other domain logic that we can encapsulate in our VO. User emails have to be a valid email:

...
public function__construct(string $string)
{
Assert::that($string)->email();
$this->string = $string;
}
...

You can see an example of how to implement single value objects in the examples directory.

Enums

Enums can be defined easily through use of the EnumTrait. Then, the enum values are simply listed as constants on the class.

finalclass Fastening implements ValueObject
{
use EnumTrait;
publicconstBUTTON = 0;
publicconstCLIP = 1;
publicconstPIN = 2;
publicconstZIP = 3;
}

When dealing with value object serialisation, the constant names are used. They are case-sensitive. So:

$fastening = Fastening::fromNative('BUTTON');
$fastening->toNative(); // Equals to string: 'BUTTON'

In code, the trait utilises magic methods to create objects based on constant name like so:

$fastening = Fastening::ZIP();
$fastening->toNative(); // Equals 'ZIP'

If your IDE supports code completion and you'd like to use named methods to create enums you can add the following PHPDoc block to your enum class:

/** * @method static Fastening BUTTON() * @method static Fastening CLIP() * @method static Fastening PIN() * @method static Fastening ZIP() */finalclass Fastening implements ValueObject

Composite value objects

A composite value object is a more complex value which is made from other values.

finalclass Location implements ValueObject
{
use CompositeTrait;
private$latitude;
private$longitude;
publicfunction__construct(Latitude$latitude, Longitude$longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
}
publicfunctiongetLatitude(): Latitude
{
return$this->latitude;
}
publicfunctiongetLongitude(): Longitude
{
return$this->longitude;
}
...

A Location is made up of two VOs (latitude, longitude). We've provided a CompositeTrait to easily implement most of the ValueObject interface automatically. It handles toNative serialistation by using reflection to return an array of all the class properties.

The CompositeTrait does not implement fromNative. We leave the construction of your object up to you.

...
public staticfunctionfromNative($native)
{
returnnewstatic(
Latitude::fromNative($native['latitude']),
Longitude::fromNative($native['longitude'])
);
}
...

You can see an example of how to implement composite objects in the examples directory.

Nulls, NonNulls and Nullables

This package allows you to deal with nullable value objects.

First create a type of value object.

interface PhoneNumber extends ValueObject
{
}

Implement a non-null version of the value object.

finalclass NonNullPhoneNumber implements PhoneNumber
{
use StringTrait;
}

Implement a null version of the value object.

finalclass NullPhoneNumber implements PhoneNumber
{
use NullTrait;
}

Implement a nullable version of the value object.

finalclass NullablePhoneNumber extends Nullable implements PhoneNumber
{
protectedstaticfunctionnonNullImplementation(): string
{
return NonNullPhoneNumber::class;
}
protectedstaticfunctionnullImplementation(): string
{
return NullPhoneNumber::class;
}
}

This 'nullable' handles automatic creation of either a null or a non-null version of the interface based on the native input. For example:

$phoneNumber = NullablePhoneNumber::fromNative(null);

The $phoneNumber above will automatically use the NullPhoneNumber implementation specified above.

Or:

$phoneNumber = NullablePhoneNumber::fromNative('+44 73715525763');

The $phoneNumber above will automatically use the NonNullPhoneNumber implementation specified above.

Sets of value objects

A set of value objects should implement the Set interface. It's just an extension of the ValueObject interface with a few simple additions.

interface Set extends ValueObject, \IteratorAggregate, \ArrayAccess, \Countable
{
publicfunctionadd($set);
publicfunctionremove($set);
publicfunctioncontains(ValueObject$value): bool;
publicfunctiontoArray(): array;
}
  • add Add values from another set to the current set.
  • remove Remove all the values contained in another set from the current set.
  • contains Returns true if the value exists in the current set.
  • toArray Returns a simple PHP array containing all of the value objects.

The other interfaces that the Set interface extends from (\IteratorAggregate, \ArrayAccess, \Countable) are for accessing the set object as though it was an array.

Non-null sets

The library provides a default implementation of the interface.

finalclass SetOfLocations extends NonNullSet implements Set
{
protectedfunctiontypeToEnforce(): string
{
return Location::class;
}
publicstaticfunctionvaluesShouldBeUnique(): bool
{
returntrue;
}
}

There are two abstract methods that need to be implemented.

  • typeToEnforce should return a string of the class name of the value object that you want to make a set of.
  • valuesShouldBeUnique should return a boolean representing whether you want to force the set to be unique.

If the set is set to unique, if duplicate values are added to the set (at instantiation or through the add method) the duplicates are filtered out.

Null and nullable sets

Just like standard value objects there are some constructs to help with creating nullable and null sets. See the Nulls, NonNulls and Nullables section for more information.

  • NullableSet The set equivalent of Nullable.
  • NullSetTrait The set equivalent of the NullTrait.

Usage of sets

Iteration, access and counting

// Iteration$set = newSetOfLocations([$one, $two]);
foreach($setas$value) {
// TODO: Do something with each value object
}
// Access$one = $set[0];
$two = $set[1];
//Counting$count = count($set); // Returns 2

add

Merges another set.

$set = newSetOfLocations([$one, $two]);
$anotherSet = newSetOfLocations([$three]);
$mergedSet = $set->add($anotherSet);
count($mergedSet) // Equals: 3

remove

Removes values from a set by using another set as reference values.

$set = newSetOfLocations([$one, $two, $three]);
$anotherSet = newSetOfLocations([$one]);
$remove = $set->remove($anotherSet);
count($remove) // Equals: 2

contains

Checks whether a set contains a particular value object.

$set = newSetOfLocations([$one, $two, $three]);
$one = newLocation(0);
$check = $set->contains($one);

About

A PHP 7 value objects helper library.

Resources

Stars

66 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - funeralzone/valueobjects: A PHP 7 value objects helper library. · GitHub
Skip to content

Latest commit

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

valueobjects

Build StatusVersion

Requirements

Requires PHP >= 7.1

Installation

Through Composer, obviously:

composer require funeralzone/valueobjects

Extensions

This library only deals with fundamental values (scalars). We've also released an extension library which provides a starting point for more complex values.

Our approach

We've written up our philosophy to PHP value objects in our A better way of writing value objects in PHP article.

Single value object

If your VO encapsulates a single value, it's most likely a scalar. We've provided some traits to deal with scalars under:

src/Scalars

Let's say you have a domain value called 'User Email'. You'd create a class which implements the ValueObject interface:

finalclass UserEmail implements ValueObject {
...

You now need to implement the interface. But because an email can essentially be considered a special kind of string (in this simple case) the StringTrait helper trait can implement most of the interface for you:

finalclass UserEmail implements ValueObject {
use StringTrait;
...

In our case, a user's email has other domain logic that we can encapsulate in our VO. User emails have to be a valid email:

...
public function__construct(string $string)
{
Assert::that($string)->email();
$this->string = $string;
}
...

You can see an example of how to implement single value objects in the examples directory.

Enums

Enums can be defined easily through use of the EnumTrait. Then, the enum values are simply listed as constants on the class.

finalclass Fastening implements ValueObject
{
use EnumTrait;
publicconstBUTTON = 0;
publicconstCLIP = 1;
publicconstPIN = 2;
publicconstZIP = 3;
}

When dealing with value object serialisation, the constant names are used. They are case-sensitive. So:

$fastening = Fastening::fromNative('BUTTON');
$fastening->toNative(); // Equals to string: 'BUTTON'

In code, the trait utilises magic methods to create objects based on constant name like so:

$fastening = Fastening::ZIP();
$fastening->toNative(); // Equals 'ZIP'

If your IDE supports code completion and you'd like to use named methods to create enums you can add the following PHPDoc block to your enum class:

/** * @method static Fastening BUTTON() * @method static Fastening CLIP() * @method static Fastening PIN() * @method static Fastening ZIP() */finalclass Fastening implements ValueObject

Composite value objects

A composite value object is a more complex value which is made from other values.

finalclass Location implements ValueObject
{
use CompositeTrait;
private$latitude;
private$longitude;
publicfunction__construct(Latitude$latitude, Longitude$longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
}
publicfunctiongetLatitude(): Latitude
{
return$this->latitude;
}
publicfunctiongetLongitude(): Longitude
{
return$this->longitude;
}
...

A Location is made up of two VOs (latitude, longitude). We've provided a CompositeTrait to easily implement most of the ValueObject interface automatically. It handles toNative serialistation by using reflection to return an array of all the class properties.

The CompositeTrait does not implement fromNative. We leave the construction of your object up to you.

...
public staticfunctionfromNative($native)
{
returnnewstatic(
Latitude::fromNative($native['latitude']),
Longitude::fromNative($native['longitude'])
);
}
...

You can see an example of how to implement composite objects in the examples directory.

Nulls, NonNulls and Nullables

This package allows you to deal with nullable value objects.

First create a type of value object.

interface PhoneNumber extends ValueObject
{
}

Implement a non-null version of the value object.

finalclass NonNullPhoneNumber implements PhoneNumber
{
use StringTrait;
}

Implement a null version of the value object.

finalclass NullPhoneNumber implements PhoneNumber
{
use NullTrait;
}

Implement a nullable version of the value object.

finalclass NullablePhoneNumber extends Nullable implements PhoneNumber
{
protectedstaticfunctionnonNullImplementation(): string
{
return NonNullPhoneNumber::class;
}
protectedstaticfunctionnullImplementation(): string
{
return NullPhoneNumber::class;
}
}

This 'nullable' handles automatic creation of either a null or a non-null version of the interface based on the native input. For example:

$phoneNumber = NullablePhoneNumber::fromNative(null);

The $phoneNumber above will automatically use the NullPhoneNumber implementation specified above.

Or:

$phoneNumber = NullablePhoneNumber::fromNative('+44 73715525763');

The $phoneNumber above will automatically use the NonNullPhoneNumber implementation specified above.

Sets of value objects

A set of value objects should implement the Set interface. It's just an extension of the ValueObject interface with a few simple additions.

interface Set extends ValueObject, \IteratorAggregate, \ArrayAccess, \Countable
{
publicfunctionadd($set);
publicfunctionremove($set);
publicfunctioncontains(ValueObject$value): bool;
publicfunctiontoArray(): array;
}
  • add Add values from another set to the current set.
  • remove Remove all the values contained in another set from the current set.
  • contains Returns true if the value exists in the current set.
  • toArray Returns a simple PHP array containing all of the value objects.

The other interfaces that the Set interface extends from (\IteratorAggregate, \ArrayAccess, \Countable) are for accessing the set object as though it was an array.

Non-null sets

The library provides a default implementation of the interface.

finalclass SetOfLocations extends NonNullSet implements Set
{
protectedfunctiontypeToEnforce(): string
{
return Location::class;
}
publicstaticfunctionvaluesShouldBeUnique(): bool
{
returntrue;
}
}

There are two abstract methods that need to be implemented.

  • typeToEnforce should return a string of the class name of the value object that you want to make a set of.
  • valuesShouldBeUnique should return a boolean representing whether you want to force the set to be unique.

If the set is set to unique, if duplicate values are added to the set (at instantiation or through the add method) the duplicates are filtered out.

Null and nullable sets

Just like standard value objects there are some constructs to help with creating nullable and null sets. See the Nulls, NonNulls and Nullables section for more information.

  • NullableSet The set equivalent of Nullable.
  • NullSetTrait The set equivalent of the NullTrait.

Usage of sets

Iteration, access and counting

// Iteration$set = newSetOfLocations([$one, $two]);
foreach($setas$value) {
// TODO: Do something with each value object
}
// Access$one = $set[0];
$two = $set[1];
//Counting$count = count($set); // Returns 2

add

Merges another set.

$set = newSetOfLocations([$one, $two]);
$anotherSet = newSetOfLocations([$three]);
$mergedSet = $set->add($anotherSet);
count($mergedSet) // Equals: 3

remove

Removes values from a set by using another set as reference values.

$set = newSetOfLocations([$one, $two, $three]);
$anotherSet = newSetOfLocations([$one]);
$remove = $set->remove($anotherSet);
count($remove) // Equals: 2

contains

Checks whether a set contains a particular value object.

$set = newSetOfLocations([$one, $two, $three]);
$one = newLocation(0);
$check = $set->contains($one);

About

A PHP 7 value objects helper library.

Resources

Stars

66 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - funeralzone/valueobjects: A PHP 7 value objects helper library. · GitHub
Skip to content

Latest commit

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

valueobjects

Build StatusVersion

Requirements

Requires PHP >= 7.1

Installation

Through Composer, obviously:

composer require funeralzone/valueobjects

Extensions

This library only deals with fundamental values (scalars). We've also released an extension library which provides a starting point for more complex values.

Our approach

We've written up our philosophy to PHP value objects in our A better way of writing value objects in PHP article.

Single value object

If your VO encapsulates a single value, it's most likely a scalar. We've provided some traits to deal with scalars under:

src/Scalars

Let's say you have a domain value called 'User Email'. You'd create a class which implements the ValueObject interface:

finalclass UserEmail implements ValueObject {
...

You now need to implement the interface. But because an email can essentially be considered a special kind of string (in this simple case) the StringTrait helper trait can implement most of the interface for you:

finalclass UserEmail implements ValueObject {
use StringTrait;
...

In our case, a user's email has other domain logic that we can encapsulate in our VO. User emails have to be a valid email:

...
public function__construct(string $string)
{
Assert::that($string)->email();
$this->string = $string;
}
...

You can see an example of how to implement single value objects in the examples directory.

Enums

Enums can be defined easily through use of the EnumTrait. Then, the enum values are simply listed as constants on the class.

finalclass Fastening implements ValueObject
{
use EnumTrait;
publicconstBUTTON = 0;
publicconstCLIP = 1;
publicconstPIN = 2;
publicconstZIP = 3;
}

When dealing with value object serialisation, the constant names are used. They are case-sensitive. So:

$fastening = Fastening::fromNative('BUTTON');
$fastening->toNative(); // Equals to string: 'BUTTON'

In code, the trait utilises magic methods to create objects based on constant name like so:

$fastening = Fastening::ZIP();
$fastening->toNative(); // Equals 'ZIP'

If your IDE supports code completion and you'd like to use named methods to create enums you can add the following PHPDoc block to your enum class:

/** * @method static Fastening BUTTON() * @method static Fastening CLIP() * @method static Fastening PIN() * @method static Fastening ZIP() */finalclass Fastening implements ValueObject

Composite value objects

A composite value object is a more complex value which is made from other values.

finalclass Location implements ValueObject
{
use CompositeTrait;
private$latitude;
private$longitude;
publicfunction__construct(Latitude$latitude, Longitude$longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
}
publicfunctiongetLatitude(): Latitude
{
return$this->latitude;
}
publicfunctiongetLongitude(): Longitude
{
return$this->longitude;
}
...

A Location is made up of two VOs (latitude, longitude). We've provided a CompositeTrait to easily implement most of the ValueObject interface automatically. It handles toNative serialistation by using reflection to return an array of all the class properties.

The CompositeTrait does not implement fromNative. We leave the construction of your object up to you.

...
public staticfunctionfromNative($native)
{
returnnewstatic(
Latitude::fromNative($native['latitude']),
Longitude::fromNative($native['longitude'])
);
}
...

You can see an example of how to implement composite objects in the examples directory.

Nulls, NonNulls and Nullables

This package allows you to deal with nullable value objects.

First create a type of value object.

interface PhoneNumber extends ValueObject
{
}

Implement a non-null version of the value object.

finalclass NonNullPhoneNumber implements PhoneNumber
{
use StringTrait;
}

Implement a null version of the value object.

finalclass NullPhoneNumber implements PhoneNumber
{
use NullTrait;
}

Implement a nullable version of the value object.

finalclass NullablePhoneNumber extends Nullable implements PhoneNumber
{
protectedstaticfunctionnonNullImplementation(): string
{
return NonNullPhoneNumber::class;
}
protectedstaticfunctionnullImplementation(): string
{
return NullPhoneNumber::class;
}
}

This 'nullable' handles automatic creation of either a null or a non-null version of the interface based on the native input. For example:

$phoneNumber = NullablePhoneNumber::fromNative(null);

The $phoneNumber above will automatically use the NullPhoneNumber implementation specified above.

Or:

$phoneNumber = NullablePhoneNumber::fromNative('+44 73715525763');

The $phoneNumber above will automatically use the NonNullPhoneNumber implementation specified above.

Sets of value objects

A set of value objects should implement the Set interface. It's just an extension of the ValueObject interface with a few simple additions.

interface Set extends ValueObject, \IteratorAggregate, \ArrayAccess, \Countable
{
publicfunctionadd($set);
publicfunctionremove($set);
publicfunctioncontains(ValueObject$value): bool;
publicfunctiontoArray(): array;
}
  • add Add values from another set to the current set.
  • remove Remove all the values contained in another set from the current set.
  • contains Returns true if the value exists in the current set.
  • toArray Returns a simple PHP array containing all of the value objects.

The other interfaces that the Set interface extends from (\IteratorAggregate, \ArrayAccess, \Countable) are for accessing the set object as though it was an array.

Non-null sets

The library provides a default implementation of the interface.

finalclass SetOfLocations extends NonNullSet implements Set
{
protectedfunctiontypeToEnforce(): string
{
return Location::class;
}
publicstaticfunctionvaluesShouldBeUnique(): bool
{
returntrue;
}
}

There are two abstract methods that need to be implemented.

  • typeToEnforce should return a string of the class name of the value object that you want to make a set of.
  • valuesShouldBeUnique should return a boolean representing whether you want to force the set to be unique.

If the set is set to unique, if duplicate values are added to the set (at instantiation or through the add method) the duplicates are filtered out.

Null and nullable sets

Just like standard value objects there are some constructs to help with creating nullable and null sets. See the Nulls, NonNulls and Nullables section for more information.

  • NullableSet The set equivalent of Nullable.
  • NullSetTrait The set equivalent of the NullTrait.

Usage of sets

Iteration, access and counting

// Iteration$set = newSetOfLocations([$one, $two]);
foreach($setas$value) {
// TODO: Do something with each value object
}
// Access$one = $set[0];
$two = $set[1];
//Counting$count = count($set); // Returns 2

add

Merges another set.

$set = newSetOfLocations([$one, $two]);
$anotherSet = newSetOfLocations([$three]);
$mergedSet = $set->add($anotherSet);
count($mergedSet) // Equals: 3

remove

Removes values from a set by using another set as reference values.

$set = newSetOfLocations([$one, $two, $three]);
$anotherSet = newSetOfLocations([$one]);
$remove = $set->remove($anotherSet);
count($remove) // Equals: 2

contains

Checks whether a set contains a particular value object.

$set = newSetOfLocations([$one, $two, $three]);
$one = newLocation(0);
$check = $set->contains($one);

About

A PHP 7 value objects helper library.

Resources

Stars

66 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - funeralzone/valueobjects: A PHP 7 value objects helper library. · GitHub
Skip to content

Latest commit

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

valueobjects

Build StatusVersion

Requirements

Requires PHP >= 7.1

Installation

Through Composer, obviously:

composer require funeralzone/valueobjects

Extensions

This library only deals with fundamental values (scalars). We've also released an extension library which provides a starting point for more complex values.

Our approach

We've written up our philosophy to PHP value objects in our A better way of writing value objects in PHP article.

Single value object

If your VO encapsulates a single value, it's most likely a scalar. We've provided some traits to deal with scalars under:

src/Scalars

Let's say you have a domain value called 'User Email'. You'd create a class which implements the ValueObject interface:

finalclass UserEmail implements ValueObject {
...

You now need to implement the interface. But because an email can essentially be considered a special kind of string (in this simple case) the StringTrait helper trait can implement most of the interface for you:

finalclass UserEmail implements ValueObject {
use StringTrait;
...

In our case, a user's email has other domain logic that we can encapsulate in our VO. User emails have to be a valid email:

...
public function__construct(string $string)
{
Assert::that($string)->email();
$this->string = $string;
}
...

You can see an example of how to implement single value objects in the examples directory.

Enums

Enums can be defined easily through use of the EnumTrait. Then, the enum values are simply listed as constants on the class.

finalclass Fastening implements ValueObject
{
use EnumTrait;
publicconstBUTTON = 0;
publicconstCLIP = 1;
publicconstPIN = 2;
publicconstZIP = 3;
}

When dealing with value object serialisation, the constant names are used. They are case-sensitive. So:

$fastening = Fastening::fromNative('BUTTON');
$fastening->toNative(); // Equals to string: 'BUTTON'

In code, the trait utilises magic methods to create objects based on constant name like so:

$fastening = Fastening::ZIP();
$fastening->toNative(); // Equals 'ZIP'

If your IDE supports code completion and you'd like to use named methods to create enums you can add the following PHPDoc block to your enum class:

/** * @method static Fastening BUTTON() * @method static Fastening CLIP() * @method static Fastening PIN() * @method static Fastening ZIP() */finalclass Fastening implements ValueObject

Composite value objects

A composite value object is a more complex value which is made from other values.

finalclass Location implements ValueObject
{
use CompositeTrait;
private$latitude;
private$longitude;
publicfunction__construct(Latitude$latitude, Longitude$longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
}
publicfunctiongetLatitude(): Latitude
{
return$this->latitude;
}
publicfunctiongetLongitude(): Longitude
{
return$this->longitude;
}
...

A Location is made up of two VOs (latitude, longitude). We've provided a CompositeTrait to easily implement most of the ValueObject interface automatically. It handles toNative serialistation by using reflection to return an array of all the class properties.

The CompositeTrait does not implement fromNative. We leave the construction of your object up to you.

...
public staticfunctionfromNative($native)
{
returnnewstatic(
Latitude::fromNative($native['latitude']),
Longitude::fromNative($native['longitude'])
);
}
...

You can see an example of how to implement composite objects in the examples directory.

Nulls, NonNulls and Nullables

This package allows you to deal with nullable value objects.

First create a type of value object.

interface PhoneNumber extends ValueObject
{
}

Implement a non-null version of the value object.

finalclass NonNullPhoneNumber implements PhoneNumber
{
use StringTrait;
}

Implement a null version of the value object.

finalclass NullPhoneNumber implements PhoneNumber
{
use NullTrait;
}

Implement a nullable version of the value object.

finalclass NullablePhoneNumber extends Nullable implements PhoneNumber
{
protectedstaticfunctionnonNullImplementation(): string
{
return NonNullPhoneNumber::class;
}
protectedstaticfunctionnullImplementation(): string
{
return NullPhoneNumber::class;
}
}

This 'nullable' handles automatic creation of either a null or a non-null version of the interface based on the native input. For example:

$phoneNumber = NullablePhoneNumber::fromNative(null);

The $phoneNumber above will automatically use the NullPhoneNumber implementation specified above.

Or:

$phoneNumber = NullablePhoneNumber::fromNative('+44 73715525763');

The $phoneNumber above will automatically use the NonNullPhoneNumber implementation specified above.

Sets of value objects

A set of value objects should implement the Set interface. It's just an extension of the ValueObject interface with a few simple additions.

interface Set extends ValueObject, \IteratorAggregate, \ArrayAccess, \Countable
{
publicfunctionadd($set);
publicfunctionremove($set);
publicfunctioncontains(ValueObject$value): bool;
publicfunctiontoArray(): array;
}
  • add Add values from another set to the current set.
  • remove Remove all the values contained in another set from the current set.
  • contains Returns true if the value exists in the current set.
  • toArray Returns a simple PHP array containing all of the value objects.

The other interfaces that the Set interface extends from (\IteratorAggregate, \ArrayAccess, \Countable) are for accessing the set object as though it was an array.

Non-null sets

The library provides a default implementation of the interface.

finalclass SetOfLocations extends NonNullSet implements Set
{
protectedfunctiontypeToEnforce(): string
{
return Location::class;
}
publicstaticfunctionvaluesShouldBeUnique(): bool
{
returntrue;
}
}

There are two abstract methods that need to be implemented.

  • typeToEnforce should return a string of the class name of the value object that you want to make a set of.
  • valuesShouldBeUnique should return a boolean representing whether you want to force the set to be unique.

If the set is set to unique, if duplicate values are added to the set (at instantiation or through the add method) the duplicates are filtered out.

Null and nullable sets

Just like standard value objects there are some constructs to help with creating nullable and null sets. See the Nulls, NonNulls and Nullables section for more information.

  • NullableSet The set equivalent of Nullable.
  • NullSetTrait The set equivalent of the NullTrait.

Usage of sets

Iteration, access and counting

// Iteration$set = newSetOfLocations([$one, $two]);
foreach($setas$value) {
// TODO: Do something with each value object
}
// Access$one = $set[0];
$two = $set[1];
//Counting$count = count($set); // Returns 2

add

Merges another set.

$set = newSetOfLocations([$one, $two]);
$anotherSet = newSetOfLocations([$three]);
$mergedSet = $set->add($anotherSet);
count($mergedSet) // Equals: 3

remove

Removes values from a set by using another set as reference values.

$set = newSetOfLocations([$one, $two, $three]);
$anotherSet = newSetOfLocations([$one]);
$remove = $set->remove($anotherSet);
count($remove) // Equals: 2

contains

Checks whether a set contains a particular value object.

$set = newSetOfLocations([$one, $two, $three]);
$one = newLocation(0);
$check = $set->contains($one);

About

A PHP 7 value objects helper library.

Resources

Stars

66 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages