{"id":2874,"date":"2022-02-02T21:53:00","date_gmt":"2022-02-02T19:53:00","guid":{"rendered":"https:\/\/nyamsprod.com\/blog\/?p=2874"},"modified":"2022-02-23T11:05:28","modified_gmt":"2022-02-23T09:05:28","slug":"understand-the-bounds-enum-in-period-v5","status":"publish","type":"post","link":"https:\/\/nyamsprod.com\/blog\/understand-the-bounds-enum-in-period-v5\/","title":{"rendered":"Understand the Bounds Enum in Period v5"},"content":{"rendered":"<div class=\"message warning\">\n<p><strong>Attention:<\/strong> Les informations de ce billet sont susceptibles d'&ecirc;tre obsol&egrave;tes car vieux de plus 2 ans.<\/p>\n<p><strong>Warning: <\/strong> The information you are reading may be obsolete, this post was published more than 2 years ago.<\/p>\n<\/div>\n<h2 class=\"wp-block-heading\" id=\"the-problem\">The Problem<\/h2>\n\n\n\n<p>Whenever I have to do some reporting I always end up having a lengthy conversation with the people who will use the reports. Because more than once, we disagree on how to calculate the reports. The disagreement stem from the fact that a report is a calculation\/selection of things over an specified interval which requires a starting endpoint and an ending endpoint. but what people always fail to realise is that a report also needs some bounds in other words we as developers need to know how to handle the report bounds. Should they be included or excluded of the report. In a previous post I have explain that <a href=\"https:\/\/nyamsprod.com\/blog\/specifying-the-endpoints-of-a-time-range\/\" title=\"I prefer to always include the start endpoint and exclude the end endpoint\">I prefer to always include the start endpoint and exclude the end endpoint<\/a>, to me it is the most reasonable default behaviour to chose.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-solution\">The solution<\/h2>\n\n\n\n<p>However as in everything there&#8217;s no single right response and context matter. Meaning that depending on the context the type of bounds chosen can change and thus affect the interval and subsequently the reports. In order to avoid or at least to reduce the changes, we need a way to express those bounds and to make sure our code can be made aware of them in an efficient and predicable way.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-initial-implementation\">The initial implementation<\/h3>\n\n\n\n<p>Luckily for us with the addition of Enumerations or <em>Enums<\/em> in <code>PHP8.1<\/code> we can easily do that as seen in the example below:<\/p>\n\n\n<script src=\"https:\/\/gist.github.com\/86be5ab4b29f9e45e06a740316cb8119.js?file=bounds-enum.php\"><\/script>\n\n\n\n<p><em>The terms lower and upper derived from how interval are expressed in mathematics.<\/em><\/p>\n\n\n\n<p>The reason we choose Enumeration over a list of scalar values is because Enumeration represents a list of finite states. In our examples bound intervals only exist in four different types. By avoiding string or integer or boolean you get a concise, readable and less error prone structure to use. You could easily make a typo with integer or with string and it would go unnoticed whilst with an Enum the PHP engine feedback loop will warn your directly of your mistake. <\/p>\n\n\n\n<p>With the defined Enums we have listed all possible bounds. When used in combination of any type of interval you can be sure that your are covering and expressing all types of bounded intervals.<\/p>\n\n\n\n<p>For instance let&#8217;s create an interval value objects which deals with integer ranges.<\/p>\n\n\n<script src=\"https:\/\/gist.github.com\/86be5ab4b29f9e45e06a740316cb8119.js?file=number-interval-dto.php\"><\/script>\n\n\n\n<p>We will be instantiating and using the immutable value object as follow<\/p>\n\n\n<script src=\"https:\/\/gist.github.com\/86be5ab4b29f9e45e06a740316cb8119.js?file=usage-basic.php\"><\/script>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"adding-some-behaviours\">Adding some behaviours<\/h3>\n\n\n\n<p>Now that we have the <code>Bounds<\/code> Enum at our disposal we can attach some behaviour to it, to improve bounds interaction. <\/p>\n\n\n\n<p>Here&#8217;s an example to illustrate, let&#8217;s say we have two <code>NumberInterval<\/code> , <code>$intervalOne<\/code> and <code>$intervalTwo<\/code> and we need to know if both intervals meets (ie: if they are touching each other). In order to satisfy those requirements, the following rules needs to be checked:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>$intervalOne<\/code> upper number must be equal to <code>$intervalTwo<\/code> lower number<\/li><li><code>$intervalOne<\/code> upper bound must be inclusive<\/li><li><code>$intervalTow<\/code> lower bound must be inclusive<\/li><\/ul>\n\n\n\n<p>Or<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>$intervalTwo<\/code> upper number must be equal to <code>$intervalOne<\/code> lower number<\/li><li><code>$intervalTwo<\/code> upper bound must be inclusive<\/li><li><code>$intervalOne<\/code> lower bound must be inclusive<\/li><\/ul>\n\n\n\n<p>Let&#8217;s expose the inclusion rules on the <code>Bounds<\/code> enum.<\/p>\n\n\n<script src=\"https:\/\/gist.github.com\/86be5ab4b29f9e45e06a740316cb8119.js?file=bounds-enum-inclusion.php\"><\/script>\n\n\n\n<p>And add the missing relation methods to the <code>NumberInterval<\/code> class.<\/p>\n\n\n<script src=\"https:\/\/gist.github.com\/86be5ab4b29f9e45e06a740316cb8119.js?file=number-interval-relation-method.php\"><\/script>\n\n\n\n<p>We now have a more powerful public API which is easier to read and to predict.<\/p>\n\n\n<script src=\"https:\/\/gist.github.com\/86be5ab4b29f9e45e06a740316cb8119.js?file=relative-method-usage.php\"><\/script>\n\n\n\n<p>As you can see the <code>NumberInterval<\/code> acts like an orchestrator who uses each of its inner property to handle basic operations to expose more powerful features to the end users.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"what-about-representation\">What about representation ?<\/h3>\n\n\n\n<p>As you may have notice our <a href=\"https:\/\/www.php.net\/manual\/en\/language.enumerations.backed.php\" title=\"Enum is not backed\">Enum is not backed<\/a>. Which means that no string representation is currently associated with it. But then again, this is not a limitation as we can work around the missing feature. Once again, the <code>Bounds<\/code> Enumeration will be responsible for the formatting while The <code>NumberInterval<\/code> will be responsible for calling the right methods on each of its properties to generate the expected string representation.<\/p>\n\n\n<script src=\"https:\/\/gist.github.com\/86be5ab4b29f9e45e06a740316cb8119.js?file=bounds-string-method.php\"><\/script>\n\n\n\n<p><em>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/ISO\/IEC_80000\" title=\"Iso 80000\">Iso 80000<\/a> is the standard to represents style guide for mathematical and scientific notation<\/em><\/p>\n\n\n\n<p>And we will add to <code>NumberInterval<\/code> a <code>toString<\/code> conversion method<\/p>\n\n\n<script src=\"https:\/\/gist.github.com\/86be5ab4b29f9e45e06a740316cb8119.js?file=number-interval-string-method.php\"><\/script>\n\n\n\n<p>Now we can represents the interval as a string.<\/p>\n\n\n<script src=\"https:\/\/gist.github.com\/86be5ab4b29f9e45e06a740316cb8119.js?file=string-method-usage.php\"><\/script>\n\n\n\n<p>If, for instance, you are using PostgreSQL you can use this string representation <a href=\"https:\/\/www.postgresql.org\/docs\/9.3\/rangetypes.html\" title=\"as part of your SQL query\">as part of your SQL query<\/a> to select some range in your persistence layer. The same could be achieved for MySQL\/MariaDB<br>with a different method, of course.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"final-thoughts\">Final thoughts<\/h3>\n\n\n\n<p>While the <code>Bounds<\/code> feature could have been implemented in previous version of PHP, by using PHP8+ feature like constructor promotion, Enum, readonly properties and the match construct, to name but a few, makes the implementation easier to reason with and improved its maintenance costs. And last but not least it makes for a better DX. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"tl-dr-1\">TL;DR<\/h2>\n\n\n\n<p>If you liked the presented <code>Bounds<\/code> Enum you can find <a href=\"https:\/\/github.com\/thephpleague\/period\/blob\/master\/src\/Bounds.php\" title=\"an improve version in the version 5\">an improved and more complete version<\/a> in the codebase of the <a href=\"https:\/\/period.thephpleague.com\/5.0\/bounds\/\"><code>League\\Period<\/code> v5 package<\/a> which will be released soon. In the previous version of <strong>League\\Period<\/strong> the bounds when constants attached to the Period class whilst in the new version they are completely separated of the class and upgraded to a proper structure to better reason with. As the Enumeration object is now fully decoupled from the rest of the package you can re-use it in your next PHP8.1+ project.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using PHP8.1 Enum feature to improve code readability and maintenance<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[5],"tags":[828,829,777,412,769],"class_list":["post-2874","post","type-post","status-publish","format-standard","hentry","category-web","tag-enumerations","tag-interval-bounds","tag-period","tag-php","tag-php-league"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/posts\/2874","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/comments?post=2874"}],"version-history":[{"count":5,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/posts\/2874\/revisions"}],"predecessor-version":[{"id":2933,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/posts\/2874\/revisions\/2933"}],"wp:attachment":[{"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/media?parent=2874"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/categories?post=2874"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nyamsprod.com\/blog\/wp-json\/wp\/v2\/tags?post=2874"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}