202 lines
8.5 KiB
YAML
202 lines
8.5 KiB
YAML
uuid:
|
|
- value: d7f7c11b-8ff6-4543-9945-fd500d9a80f9
|
|
langcode:
|
|
- value: en
|
|
type:
|
|
- target_id: daily_email
|
|
target_type: node_type
|
|
target_uuid: 8bde1f2f-eef9-4f2d-ae9c-96921f8193d7
|
|
revision_timestamp:
|
|
- value: '2025-05-11T09:00:53+00:00'
|
|
revision_uid:
|
|
- target_type: user
|
|
target_uuid: b8966985-d4b2-42a7-a319-2e94ccfbb849
|
|
revision_log: { }
|
|
status:
|
|
- value: true
|
|
uid:
|
|
- target_type: user
|
|
target_uuid: b8966985-d4b2-42a7-a319-2e94ccfbb849
|
|
title:
|
|
- value: 'Refactoring to value objects'
|
|
created:
|
|
- value: '2022-10-03T00:00:00+00:00'
|
|
changed:
|
|
- value: '2025-05-11T09:00:53+00:00'
|
|
promote:
|
|
- value: false
|
|
sticky:
|
|
- value: false
|
|
default_langcode:
|
|
- value: true
|
|
revision_translation_affected:
|
|
- value: true
|
|
path:
|
|
- alias: /daily/2022/10/03/refactoring-value-objects
|
|
langcode: en
|
|
body:
|
|
- value: |
|
|
<p>Here's a snippet of some Drupal code that I wrote last week. It's responsible for converting an array of nodes into a Collection of one of it's field values.</p>
|
|
|
|
<pre><code class="language-php">return Collection::make($stationNodes)
|
|
->map(fn (NodeInterface $station): string => $station->get('field_station_code')->getString())
|
|
->values();
|
|
</code></pre>
|
|
|
|
<p>There are two issues with this code.</p>
|
|
|
|
<p>First, whilst I'm implicitly saying that it accepts a certain type of node, because of the <code>NodeInterface</code> typehint this could accept any type of node. If that node doesn't have the required field, the code will error - but I'd like to know sooner if an incorrect type of node is passed and make it explicit that only a certain type of node can be used.</p>
|
|
|
|
<p>Second, the code for getting the field values is quite verbose and is potentially repeated in other places within the codebase. I'd like to have a simple way to access these field values that I can reuse anywhere else. If the logic for getting these particular field values changes, then I'd only need to change it in one place.</p>
|
|
|
|
<h2 id="introducing-a-value-object">Introducing a value object</h2>
|
|
|
|
<p>This is the value object that I created.</p>
|
|
|
|
<p>It accepts the original node but checks to ensure that the node is the correct type. If not, an Exception is thrown.</p>
|
|
|
|
<p>I've added a helper method to get the field value, encapsulating that logic in a reusable function whilst making the code easier to read and its intent clearer.</p>
|
|
|
|
<pre><code class="language-php">namespace Drupal\mymodule\ValueObject;
|
|
|
|
use Drupal\node\NodeInterface;
|
|
|
|
final class Station implements StationInterface {
|
|
|
|
private NodeInterface $node;
|
|
|
|
private function __construct(NodeInterface $node) {
|
|
if ($node->bundle() != 'station') {
|
|
throw new \InvalidArgumentException();
|
|
}
|
|
|
|
$this->node = $node;
|
|
}
|
|
|
|
public function getStationCode(): string {
|
|
return $this->node->get('field_station_code')->getString();
|
|
}
|
|
|
|
public static function fromNode(NodeInterface $node): self {
|
|
return new self($node);
|
|
}
|
|
|
|
}
|
|
</code></pre>
|
|
|
|
<h2 id="refactoring-to-use-the-value-object">Refactoring to use the value object</h2>
|
|
|
|
<p>This is what my code now looks like:</p>
|
|
|
|
<pre><code class="language-php">return Collection::make($stationNodes)
|
|
->map(fn (NodeInterface $node): StationInterface => Station::fromNode($node))
|
|
->map(fn (StationInterface $station): string => $station->getStationCode())
|
|
->values();
|
|
</code></pre>
|
|
|
|
<h1><<<<<<< HEAD:website/source/_daily_emails/2022-10-03.md</h1>
|
|
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<p>b9cea6d (chore: replace Sculpin with Astro):website/src/pages/daily-emails/2022-10-03.md
|
|
I've added an additional <code>map</code> to convert the nodes to the value object, but the second map can now use the new typehint - ensuring better type safety and also giving us auto-completion in IDEs and text editors. If an incorrect node type is passed in, then the Exception will be thrown and a much clearer error message will be shown.</p>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
|
|
<p>Finally, I can use the helper method to get the field value, encapsulating the logic within the value object and making it intention clearer and easier to read.</p>
|
|
|
|
|
|
format: full_html
|
|
processed: |
|
|
<p>Here's a snippet of some Drupal code that I wrote last week. It's responsible for converting an array of nodes into a Collection of one of it's field values.</p>
|
|
|
|
<pre><code class="language-php">return Collection::make($stationNodes)
|
|
->map(fn (NodeInterface $station): string => $station->get('field_station_code')->getString())
|
|
->values();
|
|
</code></pre>
|
|
|
|
<p>There are two issues with this code.</p>
|
|
|
|
<p>First, whilst I'm implicitly saying that it accepts a certain type of node, because of the <code>NodeInterface</code> typehint this could accept any type of node. If that node doesn't have the required field, the code will error - but I'd like to know sooner if an incorrect type of node is passed and make it explicit that only a certain type of node can be used.</p>
|
|
|
|
<p>Second, the code for getting the field values is quite verbose and is potentially repeated in other places within the codebase. I'd like to have a simple way to access these field values that I can reuse anywhere else. If the logic for getting these particular field values changes, then I'd only need to change it in one place.</p>
|
|
|
|
<h2 id="introducing-a-value-object">Introducing a value object</h2>
|
|
|
|
<p>This is the value object that I created.</p>
|
|
|
|
<p>It accepts the original node but checks to ensure that the node is the correct type. If not, an Exception is thrown.</p>
|
|
|
|
<p>I've added a helper method to get the field value, encapsulating that logic in a reusable function whilst making the code easier to read and its intent clearer.</p>
|
|
|
|
<pre><code class="language-php">namespace Drupal\mymodule\ValueObject;
|
|
|
|
use Drupal\node\NodeInterface;
|
|
|
|
final class Station implements StationInterface {
|
|
|
|
private NodeInterface $node;
|
|
|
|
private function __construct(NodeInterface $node) {
|
|
if ($node->bundle() != 'station') {
|
|
throw new \InvalidArgumentException();
|
|
}
|
|
|
|
$this->node = $node;
|
|
}
|
|
|
|
public function getStationCode(): string {
|
|
return $this->node->get('field_station_code')->getString();
|
|
}
|
|
|
|
public static function fromNode(NodeInterface $node): self {
|
|
return new self($node);
|
|
}
|
|
|
|
}
|
|
</code></pre>
|
|
|
|
<h2 id="refactoring-to-use-the-value-object">Refactoring to use the value object</h2>
|
|
|
|
<p>This is what my code now looks like:</p>
|
|
|
|
<pre><code class="language-php">return Collection::make($stationNodes)
|
|
->map(fn (NodeInterface $node): StationInterface => Station::fromNode($node))
|
|
->map(fn (StationInterface $station): string => $station->getStationCode())
|
|
->values();
|
|
</code></pre>
|
|
|
|
<h1><<<<<<< HEAD:website/source/_daily_emails/2022-10-03.md</h1>
|
|
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<p>b9cea6d (chore: replace Sculpin with Astro):website/src/pages/daily-emails/2022-10-03.md
|
|
I've added an additional <code>map</code> to convert the nodes to the value object, but the second map can now use the new typehint - ensuring better type safety and also giving us auto-completion in IDEs and text editors. If an incorrect node type is passed in, then the Exception will be thrown and a much clearer error message will be shown.</p>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
</blockquote>
|
|
|
|
<p>Finally, I can use the helper method to get the field value, encapsulating the logic within the value object and making it intention clearer and easier to read.</p>
|
|
|
|
|
|
summary: null
|
|
field_daily_email_cta: { }
|