ColorSpeaker was inspired by the article Tests and types [mirror] that was posted to /r/PHP two days ago.
Its aim is to be an easy-to-use converter for different types of color models (RGB, CSS hex codes, HSV, HSL, etc.). It is also meant to be a real-world example of the power of Data Transfer Objects (DTOs). I specifically created this for one of my apprentices whom I taught DTOs to the night before I read the above article, and thought this would be a perfect project for showcasing DTOs.
I developed it in three distinct iterations:
- [v0.25 tag]: A virtually literal implementation of the article's one color DTO (
RGBSpeaker
andRGBColor
). This is a great place to start learning about DTOs and is meant to be used in conjunction with the article that inspired it. - [v0.50]: I then added the
CSSHexSpeaker
class and properly moved all hex code functionality to it. But I didn't like the end-user experience (needing to use two different classes just depending on the type of color inputed). - [master]: In the third teration, I went full Composition (vs. Inheritance) [m] and Design by Contract
[m] and ended up with a single public class:
ColorSpeaker
.
tl;dr: Now you can easily load one type of color and transliterate it into another color model, effortlessly:
$hexSpeaker = ColorSpeaker::fromHexCode('#7b6F37');
$rgbColor = $hexSpeaker->toRGB();
$csvHex = "color: $hexSpeaker;"; // color: #7B6F37;
$csvRGB = "color: $rgbColor;"; // color: rgb(123, 111, 55);
This is all made possible by my SimpleDTO Project.
Want to learn even more? Check out the really complex DTO usage in my Zuora API Client. Zuora's API is not good, in the sense that their GET and POST and even PUT requests/responses do not line up with the same property names and even differ in being CamelCased or pascalCased.
Plus, their documentation is very difficult to read, in that you have to just "know" to click the > 200 OK
to see the response payloads, and what's worse, they are many levels of nested objects. To create a customer subscription, for instance, requires 6 levels-deep of nested objects.
Now, you can spend hours each day pouring back and forth between the Zuora docs and your code, like I used to, or you can write the DTOs once, and then just start typing ->
and you can quickly see via your IDE all of the possible properties, and which ones are required and not (nullable). See my test for creating Zuora Customer Accounts to see how easy complex DTOs are in practice.
Plus, isn't looking at this a lot easier than trying to grok their documents on what, exactly, is needed to create an Account?
Update: My friend suggested that I also showcase my NeverBounce API Client for a much simpler example of how to use DTOs to interface with APIs, which was what Martin Fowler originally invented them for.