177 lines
5.1 KiB
Markdown
177 lines
5.1 KiB
Markdown
|
---
|
||
|
title: Lesson 4 - Refactoring to a Repository
|
||
|
permalink: /atdc/4-refactoring-repository
|
||
|
---
|
||
|
|
||
|
{% block head_meta %}
|
||
|
<meta name="robots" content="noindex">
|
||
|
{% endblock %}
|
||
|
|
||
|
{% block content %}
|
||
|
While the existing tests are passing, let's refactor the Controller and move the logic for loading posts into a new `PostNodeRepository` class.
|
||
|
|
||
|
Doing this will make the Controller simpler and cleaner and make it easier to test that posts are returned in the correct order.
|
||
|
|
||
|
## Creating a PostNodeRepository
|
||
|
|
||
|
I like the Repository design pattern.
|
||
|
|
||
|
It's much better to have all logic to find and load nodes in one place instead of duplicating them across an application.
|
||
|
|
||
|
It also makes it easier to test.
|
||
|
|
||
|
To start, within your `atdc` module, create an `src/Repository` directory and a `PostNodeRepository.php` file inside it.
|
||
|
|
||
|
This will contain the `PostNodeRepository` class that will be responsible for loading the post nodes from the database instead of within `BlogPageController`.
|
||
|
|
||
|
Add this as the initial content:
|
||
|
|
||
|
```php
|
||
|
<?php
|
||
|
|
||
|
namespace Drupal\atdc\Repository;
|
||
|
|
||
|
use Drupal\node\NodeInterface;
|
||
|
|
||
|
final class PostNodeRepository {
|
||
|
|
||
|
/**
|
||
|
* @return array<int, NodeInterface>
|
||
|
*/
|
||
|
public function findAll(): array {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
}
|
||
|
```
|
||
|
|
||
|
I like to make my classes `final`, but this is optional and, by adding this docblock, we can specify the `findAll()` method should return an array of `NodeInterface` objects - making the code easier to read and providing better completion.
|
||
|
|
||
|
So far, you haven't changed `BlogPageController`, so the tests should still pass.
|
||
|
|
||
|
Next, let's move the logic for loading nodes into the Repository.
|
||
|
|
||
|
## Moving the logic
|
||
|
|
||
|
Remove these lines from `BlogPageController`:
|
||
|
|
||
|
```diff
|
||
|
$nodeStorage = $this->entityTypeManager()->getStorage('node');
|
||
|
$nodes = $nodeStorage->loadMultiple();
|
||
|
```
|
||
|
|
||
|
Add them to the `findAll()` method, alter the first line that gets the `EntityTypeManager` (we'll refactor this later) and return the loaded nodes:
|
||
|
|
||
|
```php
|
||
|
public function findAll(): array {
|
||
|
$nodeStorage = \Drupal::entityTypeManager()->getStorage('node');
|
||
|
$nodes = $nodeStorage->loadMultiple();
|
||
|
|
||
|
return $nodes;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Within the `BlogPageController`, create a constructor method and inject the Repository using constructor property promotion:
|
||
|
|
||
|
```php
|
||
|
public function __construct(
|
||
|
private PostNodeRepository $postNodeRepository,
|
||
|
) {
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Add `use Drupal\atdc\Repository\PostNodeRepository;` if needed, and use it to load the post nodes:
|
||
|
|
||
|
```php
|
||
|
|
||
|
public function __invoke(): array {
|
||
|
$nodes = $this->postNodeRepository->findAll();
|
||
|
|
||
|
$build = [];
|
||
|
$build['content']['#theme'] = 'item_list';
|
||
|
foreach ($nodes as $node) {
|
||
|
$build['content']['#items'][] = $node->label();
|
||
|
}
|
||
|
|
||
|
return $build;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
We're almost back to a passing test, but there's more to do.
|
||
|
|
||
|
## Getting back to green
|
||
|
|
||
|
Currently, the test is failing, as the response code is a `500` status because the `PostNodeRepository` isn't being injected into the Controller.
|
||
|
|
||
|
It's expected within the constructor, but you must add a `create` method to inject it.
|
||
|
|
||
|
```php
|
||
|
public static function create(ContainerInterface $container): self {
|
||
|
return new self(
|
||
|
$container->get(PostNodeRepository::class),
|
||
|
);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
You may also need to add `use Symfony\Component\DependencyInjection\ContainerInterface;` at the top of the file for the correct `ContainerInterface` to be used.
|
||
|
|
||
|
## Creating a service
|
||
|
|
||
|
`$container->get()` uses a service's ID to retrieve it from the container, but `PostNodeRepository` isn't in the service container.
|
||
|
|
||
|
To do this, create an `atdc.services.yml` file within your module.
|
||
|
|
||
|
Add `PostNodeRepository` using the fully-qualified class name as the service name:
|
||
|
|
||
|
```yaml
|
||
|
services:
|
||
|
Drupal\atdc\Repository\PostNodeRepository:
|
||
|
arguments: []
|
||
|
```
|
||
|
|
||
|
For now, include no arguments.
|
||
|
|
||
|
This should be enough to get the tests passing and back to green.
|
||
|
|
||
|
## Injecting more dependencies
|
||
|
|
||
|
Before moving on, let's refactor the `PostNodeRepository` and inject the `EntityTypeManager` as a dependency.
|
||
|
|
||
|
The same as the `BlogPageController`, create a constructor method and inject the `EntityTypeManagerInterface`:
|
||
|
|
||
|
```php
|
||
|
public function __construct(
|
||
|
private EntityTypeManagerInterface $entityTypeManager,
|
||
|
) {
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Add the `use Drupal\Core\Entity\EntityTypeManagerInterface;` if needed, and specify it as an argument so it's injected into the constructor:
|
||
|
|
||
|
```yaml
|
||
|
services:
|
||
|
Drupal\atdc\Repository\PostNodeRepository:
|
||
|
arguments:
|
||
|
- '@entity_type.manager'
|
||
|
```
|
||
|
|
||
|
Finally, update the code in the `findAll()` method:
|
||
|
|
||
|
```diff
|
||
|
- $nodeStorage = \Drupal::entityTypeManager()->getStorage('node');
|
||
|
+ $nodeStorage = $this->entityTypeManager->getStorage('node');
|
||
|
```
|
||
|
|
||
|
If this refactor is successful, the test will still pass.
|
||
|
|
||
|
## Conclusion
|
||
|
|
||
|
Whilst we haven't added any new tests in this lesson, we've been able to use the existing tests to ensure that the functionality still works.
|
||
|
|
||
|
If you make a mistake, the tests will fail, and you can revert the changes and try again.
|
||
|
|
||
|
If they pass, the functionality still works as expected.
|
||
|
|
||
|
Now that the `PostNodeRepository` is in place, it'll be easier for us to test the order in which the posts are returned tomorrow.
|
||
|
{% endblock %}
|