diff --git a/source/_daily_emails/2022-08-12.md b/source/_daily_emails/2022-08-12.md index c235f8b2a..d9014b2ae 100644 --- a/source/_daily_emails/2022-08-12.md +++ b/source/_daily_emails/2022-08-12.md @@ -64,7 +64,7 @@ You can also use the `COMPOSE_PROJECT_NAME` variable inside Docker Compose files For example, if you use Traefik and needed to override the host URL for a service, the string will be interpolated and the project name would be injected as you'd expect. -```yaml +```language-yaml labels: - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host( `${COMPOSE_PROJECT_NAME}.docker.localhost`, diff --git a/source/_daily_emails/2022-08-15.md b/source/_daily_emails/2022-08-15.md index 6d2435d3d..cd61eb99f 100644 --- a/source/_daily_emails/2022-08-15.md +++ b/source/_daily_emails/2022-08-15.md @@ -15,7 +15,7 @@ A Makefile contains a number of named targets that you can reference, and each h For example: -```yaml +```language-language-yaml # Start the project. start: docker-compose up -d diff --git a/source/_daily_emails/2022-09-03.md b/source/_daily_emails/2022-09-03.md index 479cec25c..89980d05e 100644 --- a/source/_daily_emails/2022-09-03.md +++ b/source/_daily_emails/2022-09-03.md @@ -15,7 +15,7 @@ By using one of these tools, you can programatically provision a new, blank serv For example, to [create a DigitalOcean droplet](https://docs.ansible.com/ansible/latest/collections/community/digitalocean/digital_ocean_module.htm): -```yaml +```language-yaml --- - community.digitalocean.digital_ocean_droplet: image: ubuntu-20-04-x64 @@ -35,7 +35,7 @@ If you needed to create a separate database server or another server for a new e [Creating an Amazon EC2 instance](https://docs.ansible.com/ansible/latest/collections/amazon/aws/ec2_instance_module.html#ansible-collections-amazon-aws-ec2-instance-module) looks very similar: -```yaml +```language-yaml --- - amazon.aws.ec2_instance: image_id: ami-123456 diff --git a/source/_daily_emails/2022-09-14.md b/source/_daily_emails/2022-09-14.md index 617b9cb88..bc6f6802e 100644 --- a/source/_daily_emails/2022-09-14.md +++ b/source/_daily_emails/2022-09-14.md @@ -10,7 +10,7 @@ I cloned a fresh version of my [Docker Examples repository](https://github.com/o I ran `mkdir -p web/modules/custom/example/tests/src/Functional` to create the directory structure that I needed, and then `touch web/modules/custom/example/tests/src/Functional/ExampleTest.php` to create a new test file and populated it with some initial code: -```php +```language-php drupalGet(''); @@ -42,7 +42,7 @@ As this is existing functionalty, the test passes. I can change either the path With the first test working, it's easy to add more for other functionality, such as whether different users should be able to access administration pages: -```php +```language-php /** @test */ public function the_admin_page_is_not_accessible_to_anonymous_users() { $this->drupalGet('admin'); diff --git a/source/_daily_emails/2022-09-16.md b/source/_daily_emails/2022-09-16.md index 4048b26ae..c071cff53 100644 --- a/source/_daily_emails/2022-09-16.md +++ b/source/_daily_emails/2022-09-16.md @@ -15,7 +15,7 @@ I find that these higher-level types of tests are easier and quicker to set up c For example, this `Device` class which is a data transfer object around Drupal's `NodeInterface`. It ensures that the correct type of node is provided, and includes a named constructor and a helper method to retrieve a device's asset ID from a field: -```php +```language-php final class Device { private NodeInterface $node; @@ -47,7 +47,7 @@ I need to specify what value is returned from the `bundle()` method as well as g I need to mock the `get()` method and specify the field name that I'm getting the value for, which also returns it's own mock for `FieldItemListInterface` with a value set for the `getString()` method. -```php +```language-php /** @test */ public function should_return_an_asset_id(): void { // Arrange. @@ -84,7 +84,7 @@ If I was to refactor from using the `get()` and `getString()` methods to a diffe This is how I could write the same test using a kernel (integration) test: -```php +```language-php /** @test */ public function should_return_an_asset_id(): void { // Arrange. diff --git a/source/_daily_emails/2022-10-03.md b/source/_daily_emails/2022-10-03.md index 82be5b659..64cef973f 100644 --- a/source/_daily_emails/2022-10-03.md +++ b/source/_daily_emails/2022-10-03.md @@ -8,7 +8,7 @@ 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 +```language-php return Collection::make($stationNodes) ->map(fn (NodeInterface $station): string => $station->get('field_station_code')->getString()) ->values(); @@ -28,7 +28,7 @@ It accepts the original node but checks to ensure that the node is the correct t 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 +```language-php namespace Drupal\mymodule\ValueObject; use Drupal\node\NodeInterface; @@ -60,7 +60,7 @@ final class Station implements StationInterface { This is what my code now looks like: -```php +```language-php return Collection::make($stationNodes) ->map(fn (NodeInterface $node): StationInterface => Station::fromNode($node)) ->map(fn (StationInterface $station): string => $station->getStationCode()) diff --git a/source/_daily_emails/2022-10-09.md b/source/_daily_emails/2022-10-09.md index 8be3f91de..21eb447da 100644 --- a/source/_daily_emails/2022-10-09.md +++ b/source/_daily_emails/2022-10-09.md @@ -37,7 +37,7 @@ It also makes it more obvious to anyone reading the code that these values are b It's similar to having this example PHP code: -```php +```language-php function __invoke(string $type, int $limit): void {}; ``` diff --git a/source/_daily_emails/2022-11-14.md b/source/_daily_emails/2022-11-14.md index ceb21c107..6fc0b0b79 100644 --- a/source/_daily_emails/2022-11-14.md +++ b/source/_daily_emails/2022-11-14.md @@ -10,7 +10,7 @@ tags: When writing object-orientated code, particularly in PHP, you usually write method names using camel-case letters - such as: -```php +```language-php public function doSomething(): void { // ... } @@ -18,7 +18,7 @@ public function doSomething(): void { This is also true when writing methods within a test class - only that the method name is prefixed with the word `test`: -```php +```language-php public function testSomething(): void { } ``` @@ -27,7 +27,7 @@ This is probably expected and complies with the PSR code style standards like PS Something that I've seen some PHP developers and some frameworks prefer is to write their test methods using snake-case letters and commonly removing the `test` prefix in favour of using an annotation: -```php +```language-php /** @test */ public function the_api_should_return_a_200_response_code_if_everything_is_ok(): void { // ... diff --git a/source/_daily_emails/2022-12-08.md b/source/_daily_emails/2022-12-08.md index 38182ae55..e0438e3e1 100644 --- a/source/_daily_emails/2022-12-08.md +++ b/source/_daily_emails/2022-12-08.md @@ -14,7 +14,7 @@ As long as a class implements an Interface, it can be decorated. For example, if I have this PHP interface: -```php +```language-php interface DoesSomething { public function doSomething(): void; @@ -23,7 +23,7 @@ interface DoesSomething I could have this class that does something: -```php +```language-php final class FirstClass implements DoesSomething { public function doSomething(): void @@ -37,7 +37,7 @@ If I need to do something else, like caching or logging the result, I can decora To do this, I need another class that implements the same interface and inject the original version. -```php +```language-php final class SecondClass implements DoesSomething { public function __constuct( diff --git a/source/_daily_emails/2022-12-10.md b/source/_daily_emails/2022-12-10.md index 13f74b377..59d394380 100644 --- a/source/_daily_emails/2022-12-10.md +++ b/source/_daily_emails/2022-12-10.md @@ -19,7 +19,7 @@ A local port needs to be exposed from the database container that Neovim can con I usually do this with a `docker-compose.override.yaml` file: -```yaml +```language-yaml services: database: ports: diff --git a/source/_daily_emails/2023-04-11.md b/source/_daily_emails/2023-04-11.md index 1a891ba8e..4f70ae6d8 100644 --- a/source/_daily_emails/2023-04-11.md +++ b/source/_daily_emails/2023-04-11.md @@ -45,7 +45,7 @@ Now I can run `make composer COMPOSER_ARGS="info drupal/core"` and see what I wa `just`, on the other hand, allows for defining parameters to its recipes: -```yaml +```language-yaml composer *args:   docker compose exec php composer {{ args }} ``` diff --git a/source/_daily_emails/2023-04-12.md b/source/_daily_emails/2023-04-12.md index 97e94c782..f0734105c 100644 --- a/source/_daily_emails/2023-04-12.md +++ b/source/_daily_emails/2023-04-12.md @@ -14,7 +14,7 @@ If I'm passing arguments into a constructor, I can declare a visibility and it w Here's an example of a value of a data transfer object that accepts a sort code and account number as strings: -```php +```language-php class AccountDetails { public function __construct( @@ -27,7 +27,7 @@ class AccountDetails { Without promoted constructor properties, I'd need to create the properties and assign them manually, and I'd have this: -```php +```language-php class AccountDetails { public string $accountNumber; diff --git a/source/_daily_emails/2023-04-13.md b/source/_daily_emails/2023-04-13.md index 8b30337dc..bb33c79f6 100644 --- a/source/_daily_emails/2023-04-13.md +++ b/source/_daily_emails/2023-04-13.md @@ -10,7 +10,7 @@ tags: Continuing with yesterday's data transfer object (DTO) example, something that can be done since PHP 8.1 is to make properties read-only: -```php +```language-php class AccountDetails { public function __construct( @@ -25,7 +25,7 @@ This means the public properties can be read and used without the need for gette Without `readonly`, a DTO can be created and the property values can be changed: -```php +```language-php $accountDetails = new AccountDetails('12345678', '00-00-00'); $accountDetails->accountNumber = 'banana'; ``` diff --git a/source/_daily_emails/2023-04-14.md b/source/_daily_emails/2023-04-14.md index 11097ea99..9e1778858 100644 --- a/source/_daily_emails/2023-04-14.md +++ b/source/_daily_emails/2023-04-14.md @@ -13,7 +13,7 @@ A data transfer object only stores data but we could also use a value object tha For example, we could validate that the account number is the correct length and the sort code is the correct format: -```php +```language-php class AccountDetails { public function __construct( diff --git a/source/_daily_emails/2023-04-16.md b/source/_daily_emails/2023-04-16.md index deebce36c..2d3225fe6 100644 --- a/source/_daily_emails/2023-04-16.md +++ b/source/_daily_emails/2023-04-16.md @@ -12,7 +12,7 @@ Marian Kostadinov ([stochnagara on Twitter](https://twitter.com/stochnagara)) re Looking at the previous class: -```php +```language-php class AccountDetails { public function __construct( @@ -25,7 +25,7 @@ class AccountDetails { Instead of setting each property as `readonly`, the whole class can instead be marked as `readonly`: -```php +```language-php readonly class AccountDetails { public function __construct( diff --git a/source/_daily_emails/2023-04-19.md b/source/_daily_emails/2023-04-19.md index e8c050476..b57585f8e 100644 --- a/source/_daily_emails/2023-04-19.md +++ b/source/_daily_emails/2023-04-19.md @@ -17,7 +17,7 @@ Why? I didn't like the inconsistency of using one approach for variable names an I'd have had code like this with a mixture of both: -```php +```language-php class MyClass { private EntityTypeManagerInterface $entityTypeManager; @@ -31,7 +31,7 @@ class MyClass { Or even more simply: -```php +```language-php $entity_type_manager = \Drupal::entityTypeManager(); ``` diff --git a/source/_daily_emails/2023-04-23.md b/source/_daily_emails/2023-04-23.md index 86614a858..3b115cef2 100644 --- a/source/_daily_emails/2023-04-23.md +++ b/source/_daily_emails/2023-04-23.md @@ -13,7 +13,7 @@ I've seen a lot on social media and posts and videos recently about Laravel Pipe This is an example from the new documentation: -```php +```language-php $user = Pipeline::send($user) ->through([ GenerateProfilePhoto::class, diff --git a/source/_daily_emails/2023-04-26.md b/source/_daily_emails/2023-04-26.md index 738134e8a..fa8c0d31d 100644 --- a/source/_daily_emails/2023-04-26.md +++ b/source/_daily_emails/2023-04-26.md @@ -21,7 +21,7 @@ Maybe a user was pending initially, and they're active after running a command o To help me get started, I'll sometimes write a test like this with placeholders to separate the test into its separate stages: -```php +```language-php /** @test */ function should_activate_a_pending_user(): void { // Arrange. @@ -38,7 +38,7 @@ This makes me think about the different stages and what each might need to conta Or I might write it out in the "Given, When, Then" format: -```php +```language-php /** @test */ function should_activate_a_pending_user(): void { // Given I have a user. diff --git a/source/_daily_emails/2023-05-17.md b/source/_daily_emails/2023-05-17.md index 4845b09d2..412859f6f 100644 --- a/source/_daily_emails/2023-05-17.md +++ b/source/_daily_emails/2023-05-17.md @@ -16,7 +16,7 @@ For example, the `drupal_set_message()` function was deprecated in Drupal 8.5 an Once it was deprecated, the function was changed to use the new service to avoid duplicating code and a message was added to notify Developers: -```php +```language-php function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) { @trigger_error('drupal_set_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931', E_USER_DEPRECATED); diff --git a/source/_daily_emails/2023-07-03.md b/source/_daily_emails/2023-07-03.md index a8fb239c4..d0d1b13f5 100644 --- a/source/_daily_emails/2023-07-03.md +++ b/source/_daily_emails/2023-07-03.md @@ -18,7 +18,7 @@ I'm using PHPUnit's native assertions to check it returns a Collection (I regula My initial implementation was to loop over each node and use `assertSame` on its bundle before refactoring to create an array of unique bundle names and comparing it to my expected names: -```php +```language-php self::assertSame( expected: [$nodeType], actual: $haystack diff --git a/source/_daily_emails/2023-08-17.md b/source/_daily_emails/2023-08-17.md index 32437e413..c51105780 100644 --- a/source/_daily_emails/2023-08-17.md +++ b/source/_daily_emails/2023-08-17.md @@ -13,7 +13,7 @@ Writing custom assertions is a great way to clean up your test code. Here's an example from one of my client Drupal projects: -```php +```language-php private static function assertProductVariationHasPrice(ProductVariationInterface $productVariation, string $expectedPrice): void { self::assertSame( actual: $productVariation->getPrice()->getNumber(), diff --git a/source/_daily_emails/2023-08-19.md b/source/_daily_emails/2023-08-19.md index 3668c5c69..c312fa1e3 100644 --- a/source/_daily_emails/2023-08-19.md +++ b/source/_daily_emails/2023-08-19.md @@ -14,7 +14,7 @@ In PHP, this is done by throwing an Exception if a condition is met. For example: -```php +```language-php if (!is_array(false)) { throw new \Exception('Not an array'); } @@ -22,13 +22,13 @@ if (!is_array(false)) { There's also the `assert` construct which, since PHP 8.0, throws an Exception by default: -```php +```language-php assert(is_array(false)); ``` You can also use an assertion library, such as `webmozart/assert` or `beberlei/assert` which provide assertions and guard methods: -```php +```language-php use Webmozart\Assert\Assert; Assert::isArray(false); diff --git a/source/_daily_emails/2023-08-20.md b/source/_daily_emails/2023-08-20.md index 10ec3b12c..b68d8d433 100644 --- a/source/_daily_emails/2023-08-20.md +++ b/source/_daily_emails/2023-08-20.md @@ -12,7 +12,7 @@ Following yesterday's email about input validation, guard clauses and assertion For example: -```php +```language-php function createJourney(string $from, string $to, int $duration): void { var_dump($from, $to, $duration); } @@ -22,13 +22,13 @@ In this code, each parameter has a type, but there's no validation on the values If I run this: -```plain +```language-plain createJourney('', '', -10); ``` I would get this output: -```plain +```language-plain string(0) "" string(0) "" int(-10) @@ -44,7 +44,7 @@ I can use an assertion library or throw my own Exceptions if the values pass the For example: -```php +```language-php function createJourney(string $from, string $to, int $duration): void { Assert::stringNotEmpty($from); Assert::stringNotEmpty($to); diff --git a/source/_daily_emails/2023-09-07.md b/source/_daily_emails/2023-09-07.md index abf909305..d0cf64f61 100644 --- a/source/_daily_emails/2023-09-07.md +++ b/source/_daily_emails/2023-09-07.md @@ -26,7 +26,7 @@ To start, we wrote a test for existing functionality within Drupal core - anonym This is the whole test: -```php +```language-php getClassReflection()->getName(); if ($callingClass === TextMessageQueueProcessor::class) { diff --git a/source/_daily_emails/2023-10-02.md b/source/_daily_emails/2023-10-02.md index 86c1ba43f..4da28fd95 100644 --- a/source/_daily_emails/2023-10-02.md +++ b/source/_daily_emails/2023-10-02.md @@ -23,7 +23,7 @@ Only write comments that add value, and if there are fewer comments, they are ea This is an example from an open source pull request: -```php +```language-php /** * An interface for field access override. */ diff --git a/source/_daily_emails/2023-10-10.md b/source/_daily_emails/2023-10-10.md index 81f493467..c20c490f4 100644 --- a/source/_daily_emails/2023-10-10.md +++ b/source/_daily_emails/2023-10-10.md @@ -38,7 +38,7 @@ For example, to install Pathauto, you run `composer require drupal/pathauto`. Within its output, you'll see this: -```plain +```language-plain Package operations: 3 installs, 0 updates, 0 removals - Downloading drupal/token (1.12.0) - Downloading drupal/ctools (4.0.4) diff --git a/source/_daily_emails/2023-11-16.md b/source/_daily_emails/2023-11-16.md index 94ddeb089..65a9ed9e0 100644 --- a/source/_daily_emails/2023-11-16.md +++ b/source/_daily_emails/2023-11-16.md @@ -16,7 +16,7 @@ Here's an example (thanks, ChatGPT, for the code). ## The Class to be tested (MyClass.php) -```php +```language-php false]): void {      echo 'Hello, ' . $name; } @@ -53,7 +53,7 @@ Then you'll get this error: Now, we can use a docblock to provide more information to PHPStan to describe the structure of the `$options` array and that it has strings for keys and boolean values. -```php +```language-php /**  * @param array $options  */ @@ -61,7 +61,7 @@ Now, we can use a docblock to provide more information to PHPStan to describe th Although it's not erroring, we can add the `$name` parameter and declare it as a `string`. -```php +```language-php /**  * @param string $name  * @param array $options @@ -78,7 +78,7 @@ We don't want an empty string for a name and want the correct options and values Let's change the docblock to this: -```php +```language-php /**  * @param non-empty-string $name  * @param array{shout: bool} $options diff --git a/source/_daily_emails/2023-11-27.md b/source/_daily_emails/2023-11-27.md index 500aa63fe..28dc77488 100644 --- a/source/_daily_emails/2023-11-27.md +++ b/source/_daily_emails/2023-11-27.md @@ -16,7 +16,7 @@ As well as different base classes for types of tests - i.e. functional, kernel a For example, if we have this test: -```php +```language-php drupalGet('/'); diff --git a/source/_daily_emails/2024-01-18.md b/source/_daily_emails/2024-01-18.md index ea8c93453..baf7b791e 100644 --- a/source/_daily_emails/2024-01-18.md +++ b/source/_daily_emails/2024-01-18.md @@ -15,7 +15,7 @@ In some instances, setting zero will return all items - essentially, an 'unlimit I imagine the code looks something like this: -```php +```language-php if ($limit > 0) { $query->range(0, $limit); } @@ -31,7 +31,7 @@ It means the value could be either an integer or null, but I think the intent of This would make the code look like this: -```php +```language-php if (is_int($limit)) { $query->range(0, $limit); } @@ -45,7 +45,7 @@ It wouldn't make sense to set a negative number as the limit or, as the unlimite This is the end code I'd likely write: -```php +```language-php if (is_int($limit)) { if ($limit < 1) { throw new InvalidArgumentException('A limit must be a positive integer.'); diff --git a/source/_daily_emails/2024-01-27.md b/source/_daily_emails/2024-01-27.md index 7372c1512..aa085c544 100644 --- a/source/_daily_emails/2024-01-27.md +++ b/source/_daily_emails/2024-01-27.md @@ -17,14 +17,14 @@ The default approach is that all files can be added, and you specify the files a For example, if my `.gitignore` file was this, these two directories would be ignored: -```plain +```language-plain vendor web ``` The other approach is to ignore everything and unignore the things to add. For example: -```plain +```language-plain * !build.yaml !Dockerfile diff --git a/source/_daily_emails/2024-01-28.md b/source/_daily_emails/2024-01-28.md index 69e1325d5..e4b004ad2 100644 --- a/source/_daily_emails/2024-01-28.md +++ b/source/_daily_emails/2024-01-28.md @@ -27,7 +27,7 @@ Anything that affects multiple users - such as ignoring `vendor` or `node_module Add this to your `~/.gitconfig` or `~/.config/git/config` file to set the path for your global ignore file: -```plain +```language-plain [core] excludesFile = "~/.config/git/ignore" ``` diff --git a/source/_daily_emails/2024-02-08.md b/source/_daily_emails/2024-02-08.md index 09dc6e744..0cb8385bb 100644 --- a/source/_daily_emails/2024-02-08.md +++ b/source/_daily_emails/2024-02-08.md @@ -23,7 +23,7 @@ Going forward, I'd like to ensure that each Drupal module only uses its own clas Here's what I have so far for my [testing course codebase][atdc]: -```php +```language-php final class ArchitectureTest { public function test_classes_should_be_final(): Rule { diff --git a/source/_layouts/base.html.twig b/source/_layouts/base.html.twig index 3d564e31c..abf7e4770 100644 --- a/source/_layouts/base.html.twig +++ b/source/_layouts/base.html.twig @@ -37,8 +37,12 @@ {% endif %} {% endblock %} + + {% block styles %}{% endblock %} {% block body %}{% endblock %} + + {% block scripts %}{% endblock %} diff --git a/source/_layouts/page.html.twig b/source/_layouts/page.html.twig index 3d01df5d3..67aa7409e 100644 --- a/source/_layouts/page.html.twig +++ b/source/_layouts/page.html.twig @@ -3,3 +3,20 @@ {% block content_bottom %} {% include 'about-me.html.twig' %} {% endblock %} + +{% block styles %} + + + +{% endblock %} + +{% block scripts %} + + + + +{% endblock %} diff --git a/source/_pages/atdc/1.md b/source/_pages/atdc/1.md index c08ec4719..dc5dab8b0 100644 --- a/source/_pages/atdc/1.md +++ b/source/_pages/atdc/1.md @@ -30,7 +30,7 @@ Before adding tests, you must create a module to place them in. Run `mkdir -p web/modules/custom/atdc` to create an empty module directory and create an `atdc.info.yml` file within it with this content: -```yaml +```language-yaml name: ATDC type: module core_version_requirement: ^10 @@ -47,7 +47,7 @@ Run `mkdir -p web/modules/custom/atdc/tests/src/Functional && touch web/modules/ Then, add this content. -```php +```language-php drupalGet('/'); diff --git a/source/_pages/atdc/10.md b/source/_pages/atdc/10.md index b8f779a5f..c497b1bf6 100644 --- a/source/_pages/atdc/10.md +++ b/source/_pages/atdc/10.md @@ -18,7 +18,7 @@ Let's see how that would look as a unit test. Create a new test, `PostNodeRepositoryUnitTest` and, for now, just create a new `PostNodeRepository`: -```php +```language-php createMock(EntityTypeManagerInterface::class), ); @@ -73,7 +73,7 @@ As the mock implements `EntityTypeManagerInterface`, this will fix the failure, Next, try to get the posts from the Repository: -```php +```language-php $repository->findAll(); ``` @@ -87,7 +87,7 @@ For the test to work, this needs to be mocked too and returned from the `getStor Create a mock of `EntityStorageInterface`, which will be used as the node storage: -```php +```language-php $nodeStorage = $this->createMock(EntityStorageInterface::class); ``` @@ -95,7 +95,7 @@ Next, this needs to be returns from the mock `EntityTypeManager`. To do this, specify that the `getStorage()` method when called with the value `node`, will return the mocked node storage: -```php +```language-php $entityTypeManager = $this->createMock(EntityTypeManagerInterface::class); $entityTypeManager->method('getStorage')->with('node')->willReturn($nodeStorage); @@ -112,7 +112,7 @@ You'll need to use a mock for each node and set what each method needs to return The same as the Kernel test, set a title for each post with different created times. -```php +```language-php $node1 = $this->createMock(NodeInterface::class); $node1->method('bundle')->willReturn('post'); $node1->method('getCreatedTime')->willReturn(strtotime('-1 week')); @@ -131,7 +131,7 @@ $node3->method('label')->willReturn('Post three'); Then, specify the `loadByProperties` method should return the posts. -```php +```language-php $nodeStorage->method('loadByProperties')->willReturn([ $node1, $node2, @@ -141,7 +141,7 @@ $nodeStorage->method('loadByProperties')->willReturn([ Finally, add some assertions that the nodes returned are the correct ones and in the correct order: -```php +```language-php $posts = $repository->findAll(); self::assertContainsOnlyInstancesOf(NodeInterface::class, $posts); @@ -166,7 +166,7 @@ This is testing the same thing as the kernel test, but it's your preference whic Hopefully, if you run your whole testsuite, you should see output like this: -```plain +```language-plain PHPUnit 9.6.15 by Sebastian Bergmann and contributors. ......... 9 / 9 (100%) @@ -176,7 +176,7 @@ Time: 00:07.676, Memory: 10.00 MB Or, if you use `--testdox`, output like this: -```plain +```language-plain PHPUnit 9.6.15 by Sebastian Bergmann and contributors. Blog Page (Drupal\Tests\atdc\Functional\BlogPage) diff --git a/source/_pages/atdc/2.md b/source/_pages/atdc/2.md index f2e120771..9a22067cd 100644 --- a/source/_pages/atdc/2.md +++ b/source/_pages/atdc/2.md @@ -22,7 +22,7 @@ For example, how do we test the administration pages to see if they work for a u Let's start with a new test: -```php +```language-php public function testAdminPageLoggedIn(): void { $this->drupalGet('/admin'); @@ -47,7 +47,7 @@ This is commonly known as the **Arrange** step of the test. To create a user, use `$this->drupalCreateUser()` and `$this->drupalLogin()` to log in as that user. -```php +```language-php $user = $this->drupalCreateUser(); $this->drupalLogin($user); @@ -61,7 +61,7 @@ As we're testing against a temporary Drupal installation, we don't have access t To do this, when creating the user, include an array of permissions to add to it: -```php +```language-php $user = $this->createUser(permissions: [ 'access administration pages', 'administer site configuration', @@ -80,7 +80,7 @@ Let's create a page and test we can view it. Firstly, let's ensure the page is not found: -```php +```language-php public function testContent(): void { $this->drupalGet('/node/1'); $this->assertSession()->statusCodeEquals(Response::HTTP_NOT_FOUND); @@ -91,7 +91,7 @@ Similar to `$this->createUser()`, there are similar methods to create content ty Again, as there are no existing content or content types, we need to create them and add the follow-up assertions: -```php +```language-php public function testContent(): void { // ... @@ -111,7 +111,7 @@ You're probably expecting the test to pass now, but you'll likely get an error l To fix this, we need to tell Drupal to enable the `node` module within the test by adding this within the test class: -```php +```language-php protected static $modules = ['node']; ``` @@ -123,7 +123,7 @@ Here's a tip for today: if you're getting an unexpected status code or another e To do that, add this to your test, and it will output the page content: -```php +```language-php var_dump($this->getSession()->getPage()->getContent()); ``` diff --git a/source/_pages/atdc/3.md b/source/_pages/atdc/3.md index 6f434b7e3..89c55f079 100644 --- a/source/_pages/atdc/3.md +++ b/source/_pages/atdc/3.md @@ -20,7 +20,7 @@ Create a new `BlogPageTest` and have it extend `BrowserTestBase`. Let's assert that a page should exist at `/blog` by returning a `200` status code, as this should be accessible by anonymous users. -```php +```language-php createNode(['type' => 'post', 'title' => 'First post']); @@ -165,7 +165,7 @@ Start by extending the `ControllerBase` base class within your Controller: Now, within the `__invoke` method, add this to return a list of each node title: -```php +```language-php public function __invoke(): array { $nodeStorage = $this->entityTypeManager()->getStorage('node'); $nodes = $nodeStorage->loadMultiple(); diff --git a/source/_pages/atdc/4.md b/source/_pages/atdc/4.md index 4a287f521..d3cccbe40 100644 --- a/source/_pages/atdc/4.md +++ b/source/_pages/atdc/4.md @@ -26,7 +26,7 @@ This will contain the `PostNodeRepository` class that will be responsible for lo Add this as the initial content: -```php +```language-php 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 +```language-php public function findAll(): array { $nodeStorage = \Drupal::entityTypeManager()->getStorage('node'); $nodes = $nodeStorage->loadMultiple(); @@ -73,7 +73,7 @@ public function findAll(): array { Within the `BlogPageController`, create a constructor method and inject the Repository using constructor property promotion: -```php +```language-php public function __construct( private PostNodeRepository $postNodeRepository, ) { @@ -82,7 +82,7 @@ public function __construct( Add `use Drupal\atdc\Repository\PostNodeRepository;` if needed, and use it to load the post nodes: -```php +```language-php public function __invoke(): array { $nodes = $this->postNodeRepository->findAll(); @@ -105,7 +105,7 @@ Currently, the test is failing, as the response code is a `500` status because t It's expected within the constructor, but you must add a `create` method to inject it. -```php +```language-php public static function create(ContainerInterface $container): self { return new self( $container->get(PostNodeRepository::class), @@ -123,7 +123,7 @@ 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 +```language-yaml services: Drupal\atdc\Repository\PostNodeRepository: arguments: [] @@ -139,7 +139,7 @@ Before moving on, let's refactor the `PostNodeRepository` and inject the `Entity The same as the `BlogPageController`, create a constructor method and inject the `EntityTypeManagerInterface`: -```php +```language-php public function __construct( private EntityTypeManagerInterface $entityTypeManager, ) { @@ -148,7 +148,7 @@ public function __construct( Add the `use Drupal\Core\Entity\EntityTypeManagerInterface;` if needed, and specify it as an argument so it's injected into the constructor: -```yaml +```language-yaml services: Drupal\atdc\Repository\PostNodeRepository: arguments: diff --git a/source/_pages/atdc/5.md b/source/_pages/atdc/5.md index 62ed07a1a..97334a916 100644 --- a/source/_pages/atdc/5.md +++ b/source/_pages/atdc/5.md @@ -28,7 +28,7 @@ Instead of making HTTP requests and checking the responses, we can test the resu Let's create a new test that uses the `PostNodeRepository` to find the nodes and assert we get an expected number returned. -```php +```language-php createNode()` to create posts. Create the three posts the test is expecting: -```php +```language-php // Arrange. $this->createNode(['type' => 'post']); $this->createNode(['type' => 'post']); @@ -126,7 +126,7 @@ Next, let's assert they're returned in a specific order. Update the posts to have a specific title and created date so we can specify which order we expect them to be returned in and which titles they should have: -```php +```language-php // Arrange. $this->createNode([ 'created' => (new DrupalDateTime('-1 week'))->getTimestamp(), @@ -151,7 +151,7 @@ Note we're intentionally setting them to be in an incorrect order, to begin with Next, assert that the titles are returned in the correct order. -```php +```language-php self::assertSame( ['Post two', 'Post one', 'Post three'], array_map( @@ -165,7 +165,7 @@ For each node in `$nodes`, get its label (title) and compare them with the title As expected, the test fails: -```plain +```language-plain 1) Drupal\Tests\atdc\Kernel\PostNodeRepositoryTest::testPostsAreReturnedByCreatedDate Failed asserting that two arrays are identical. --- Expected @@ -187,7 +187,7 @@ We need to update the code within `PostNodeRepository` to fix the ordering. After loading the nodes, we need to sort them. -```php +```language-php public function findAll(): array { $nodeStorage = $this->entityTypeManager->getStorage('node'); $nodes = $nodeStorage->loadMultiple(); @@ -206,7 +206,7 @@ This gets us further, but the test is still failing. Whilst the order is correct, the array keys don't match what we expect: -```plain +```language-plain 1) Drupal\Tests\atdc\Kernel\PostNodeRepositoryTest::testPostsAreReturnedByCreatedDate Failed asserting that two arrays are identical. --- Expected @@ -233,7 +233,7 @@ And, because the correct titles are still being shown, our original Functional t Tip: to see the names of the tests in your output, add the `--testdox` flag to the `phpunit` command: -```plain +```language-plain Blog Page (Drupal\Tests\atdc\Functional\BlogPage) ✔ Blog page ✔ Posts are visible diff --git a/source/_pages/atdc/6.md b/source/_pages/atdc/6.md index 585bc7c69..06f47e349 100644 --- a/source/_pages/atdc/6.md +++ b/source/_pages/atdc/6.md @@ -12,7 +12,7 @@ In yesterday's lesson, you created your first Kernel test and used it to ensure This is how we're creating the posts currently: -```php +```language-php $this->createNode([ 'created' => (new DrupalDateTime('-1 week'))->getTimestamp(), 'title' => 'Post one', @@ -40,7 +40,7 @@ Let's create a Builder class to create the posts. This is how I'd like to create a post using a `PostBuilder`: -```php +```language-php PostBuilder::create() ->setCreatedDate('-1 week') ->setTitle('Post one') @@ -51,7 +51,7 @@ This makes it easier to do by creating named methods for each value we want to s To do this, create a new class at `src/Builder/PostBuilder.php`: -```php +```language-php $this->created?->getTimestamp(), @@ -111,7 +111,7 @@ public function getPost(): NodeInterface { Now, refactor the test to use the `PostBuilder`: -```php +```language-php PostBuilder::create() ->setCreatedDate('-1 week') ->setTitle('Post one') @@ -136,7 +136,7 @@ Finally, for today, let's refactor the assertion that verifies the titles are re This is the current assertion: -```php +```language-php self::assertSame( ['Post two', 'Post one', 'Post three'], array_map( @@ -152,7 +152,7 @@ We can make this more reusable and readable by extracting this into a new custom Create a new static function at the bottom of the class with a name that describes what it's asserting: -```php +```language-php /** * @param array $expectedTitles * @param array $nodes @@ -179,7 +179,7 @@ The benefits are that this now has a name that describes what we're asserting, a Finally, refactor the test to use the new assertion: -```php +```language-php self::assertNodeTitlesAreSame( ['Post two', 'Post one', 'Post three'], $nodes, diff --git a/source/_pages/atdc/7.md b/source/_pages/atdc/7.md index 200a178cd..2d45a36d3 100644 --- a/source/_pages/atdc/7.md +++ b/source/_pages/atdc/7.md @@ -20,7 +20,7 @@ First, let's ensure that only published nodes are returned and displayed on the We can do this easily with a functional test, so add a new test method to `BlogPostTest`: -```php +```language-php public function testOnlyPublishedNodesAreShown(): void { PostBuilder::create() ->setTitle('Post one') @@ -54,7 +54,7 @@ In this test, we want to create some published and unpublished posts and assert To fix the error, add this function so it exists: -```php +```language-php public function isPublished(): self { return $this; } @@ -70,7 +70,7 @@ When using `PostBuilder` in the previous lesson, we were always providing a crea Update the `getPost()` method to only set the created time if the `created` property has a value. -```php +```language-php public function getPost(): NodeInterface { $post = Node::create([ 'title' => $this->title, @@ -93,7 +93,7 @@ Now, we can see a similar error to the one before for `isNotPublished()`. Again, create the simplest version of the method so the test can progress: -```php +```language-php public function isNotPublished(): self { return $this; } @@ -112,13 +112,13 @@ Within `PostBuilder`, we need to use the `isPublished` and `isNotPublished` meth First, add an `isPublished` property to the class and set it to be `TRUE` by default: -```php +```language-php private bool $isPublished = TRUE; ``` Next, update the `isPublished()` and `isNotPublished()` methods to set the value appropriately: -```php +```language-php public function isNotPublished(): self { $this->isPublished = FALSE; @@ -136,7 +136,7 @@ Even though `isPublished` is already true by default, doing this makes it explic Finally, within `getPost()`, update the code that creates the node to set the `status` property accordingly. -```php +```language-php $post = Node::create([ 'status' => $this->isPublished, 'title' => $this->title, @@ -152,7 +152,7 @@ We also need to update the `PostNodeRepository` as that is responsible for loadi Currently, all we're doing is this: -```php +```language-php $nodes = $nodeStorage->loadMultiple(); ``` @@ -160,7 +160,7 @@ This will load all nodes, regardless of their type or status. To fix this, change this to use `loadByProperties()` instead: -```php +```language-php $nodes = $nodeStorage->loadByProperties(); ``` @@ -170,7 +170,7 @@ Note: you can also use `->getQuery()` if you prefer and write the query yourself For this case, let's add a property for `status` and its value to be `TRUE`: -```php +```language-php $nodes = $nodeStorage->loadByProperties([ 'status' => TRUE, ]); @@ -184,7 +184,7 @@ The other issue is all published nodes are returned, even if they aren't posts. Before adding this to `PostNodeRepository`, create a new failing test for it: -```php +```language-php public function testOnlyPostNodesAreShown(): void { PostBuilder::create()->setTitle('Post one')->getPost(); PostBuilder::create()->setTitle('Post two')->getPost(); @@ -213,7 +213,7 @@ If you run the test, it should fail as expected: Now we have a failing test, let's add the extra condition to `PostNodeRepository`: -```php +```language-php $nodes = $nodeStorage->loadByProperties([ 'status' => TRUE, 'type' => 'post', diff --git a/source/_pages/atdc/8.md b/source/_pages/atdc/8.md index 9a16a9b25..591e189f5 100644 --- a/source/_pages/atdc/8.md +++ b/source/_pages/atdc/8.md @@ -12,7 +12,7 @@ In this lesson, let's add tags to our posts using the `PostBuilder`. As we're doing test-driven development, start by creating a new `PostBuilderTest`: -```php +```language-php installConfig(modules: [ 'atdc_test', ]); @@ -218,7 +218,7 @@ $this->installConfig(modules: [ After adding this and attempting to install the configuration to add the field, you'll get an error: -```plain +```language-plain Exception when installing config for module atdc_test, the message was: Field 'field_tags' on entity type 'node' references a target entity type 'taxonomy_term', which does not exist. ``` @@ -234,7 +234,7 @@ As well as the field configuration, we also need to create the Post content type This can be done by creating a `node.type.post.yml` file: -```yaml +```language-yaml langcode: en status: true dependencies: { } @@ -255,7 +255,7 @@ Let's update the test and add assertions about the tags being saved and returned Get the tags from the post and assert that three tags are returned: -```php +```language-php $tags = $node->get('field_tags')->referencedEntities(); self::assertCount(3, $tags); ``` @@ -264,7 +264,7 @@ As none have been added, this would fail the test. Update the test to use a `setTags()` method that you haven't created yet: -```php +```language-php $node = PostBuilder::create() ->setTitle('test') ->setTags(['Drupal', 'PHP', 'Testing']) @@ -277,7 +277,7 @@ You should get an error confirming the method is undefined: To fix this, add the `tags` property and `setTags()` method to `PostBuilder`: -```php +```language-php /** * @var string[] */ @@ -297,7 +297,7 @@ Tags will be an array of strings, and `setTags()` should set the tags to the `ta Next, add the logic to `getPost()` to create a taxonomy term for each tag name. -```php +```language-php $tagTerms = []; if ($this->tags !== []) { @@ -322,7 +322,7 @@ If `$this->tags` is not empty, create a new taxonomy term for each one and save As well as asserting we have the correct number of tags, let's also assert that the correct tag names are returned and that they are the correct type of term. -```php +```language-php self::assertContainsOnlyInstancesOf(TermInterface::class, $tags); foreach ($tags as $tag) { self::assertSame('tags', $tag->bundle()); @@ -333,7 +333,7 @@ To assert the tags array only includes taxonomy terms, use `self::assertContains Next, add some new assertions to the test to check the tag names match the specified tags. -```php +```language-php self::assertSame('Drupal', $tags[0]->label()); self::assertSame('PHP', $tags[1]->label()); self::assertSame('Testing', $tags[2]->label()); diff --git a/source/_pages/atdc/9.md b/source/_pages/atdc/9.md index 9d9fb719c..9897c866f 100644 --- a/source/_pages/atdc/9.md +++ b/source/_pages/atdc/9.md @@ -22,7 +22,7 @@ I've also seen Unit tests that are very tightly coupled to the implementation, s Based on what you've learned so far, let's write a Unit test that we'd expect to pass: -```php +```language-php createMock(NodeInterface::class); @@ -69,7 +69,7 @@ public function it_wraps_a_post(): void { Next, add an assertion to ensure the bundle is correct: -```php +```language-php self::assertSame('post', $node->bundle()); ``` @@ -81,7 +81,7 @@ Because you're using a mock, all methods will return `NULL`. To get this to pass, you need to define what `$this->bundle()` will return: -```php +```language-php $node->method('bundle')->willReturn('post'); ``` @@ -97,7 +97,7 @@ Within the test, instantiate a new `Postwrapper` class that takes the node as an Then, add an assertion that a `getType()` method should return `post`. -```php +```language-php $wrapper = new PostWrapper($node); self::assertSame('post', $wrapper->getType()); @@ -105,7 +105,7 @@ self::assertSame('post', $wrapper->getType()); Next, create a `PostWrapper` class with the `getType()` method: -```php +```language-php