docs(daily-email): add 2022-10-03
This commit is contained in:
parent
1c317feeac
commit
3407a052b0
71
website/source/_daily_emails/2022-10-03.md
Normal file
71
website/source/_daily_emails/2022-10-03.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
title: Refactoring to value objects
|
||||
date: "2022-10-03"
|
||||
permalink: /archive/2022/10/03/refactoring-value-objects
|
||||
tags: [php]
|
||||
---
|
||||
|
||||
|
||||
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.
|
||||
|
||||
```php
|
||||
return Collection::make($stationNodes)
|
||||
->map(fn (NodeInterface $station): string => $station->get('field_station_code')->getString())
|
||||
->values();
|
||||
```
|
||||
|
||||
There are two issues with this code.
|
||||
|
||||
First, whilst I'm implicitly saying that it accepts a certain type of node, because of the `NodeInterface` 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.
|
||||
|
||||
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.
|
||||
|
||||
## Introducing a value object
|
||||
|
||||
This is the value object that I created.
|
||||
|
||||
It accepts the original node but checks to ensure that the node is the correct type. If not, an Exception is thrown.
|
||||
|
||||
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.
|
||||
|
||||
```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);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Refactoring to use the value object
|
||||
|
||||
This is what my code now looks like:
|
||||
|
||||
```php
|
||||
return Collection::make($stationNodes)
|
||||
->map(fn (NodeInterface $node): StationInterface => Station::fromNode($node))
|
||||
->map(fn (StationInterface $station): string => $station->getStationCode())
|
||||
->values();
|
||||
```
|
||||
I've added an additional `map` 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.
|
||||
|
||||
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.
|
Loading…
Reference in a new issue