Add ATDC course pages
This commit is contained in:
parent
11ae0f9fc7
commit
72c3d50cf7
11 changed files with 2178 additions and 0 deletions
176
source/_pages/atdc/4.md
Normal file
176
source/_pages/atdc/4.md
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
---
|
||||
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 %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue