Minimalistic Way to Create Your First Nette Extension

Nette extension allows you not only to create open-source packages, but also to split your application to small and logical chunks of code.

Open-source extensions are more complex using many Nette\DI features, but today I will show you, how to start with one Nette\DI method and one service only.

I consider Nette Sandbox the best way to show learn any Nette feature. Let's use it.

Register service in Nette Sandbox

If you want to register a service, what will you do?

# app/config/config.neon

services:
    - App\Model\UserManager # put it there

File app/config/config.neon is like a socket.

There is one place to active your computer, when you plug it in.

But what if your want to plug in computer and mobile charger in the same time?

To load more services, we use the same interface as app/config/config.neon: a file with service section that lists services.

Create Local Extension in 5 Steps

Let's say we want to decouple a FileSystem utilities.

1. Pick a Name for Directory

What about "FileSystem"? If you agree, create src/FileSystem directory. We will put configuration (sevices.neon) and classes there.

2. Create or move Related Classes there

Starting small, one service will do. When it grows, we can decouple it later.

# src/FileSystem/FileSystem.php

<?php declare(strict_types=1);

namespace FileSystem;

final class FileSystem
{
    // some awesome methods!
}

3. Create config

This is similar to app/config/services.neon, just in different location:

# src/FileSystem/config/services.neon

- FileSystem\FileSystem

4. Create an Extension

# src/FileSystem/DI/FileSystemExtension.php

<?php declare(strict_types=1);

namespace FileSystem\DI;

use Nette\DI\Compiler;
use Nette\DI\CompilerExtension;

final class FileSystemExtension extends CompilerExtension
{
    public function loadConfiguration()
    {
        // this method loads servcies from config and registers them do Nette\DI Container
        Compiler::loadDefinitions(
            $this->getContainerBuilder(),
            $this->loadFromFile(__DIR__.'/../config/services.neon')
        );
    }
}

5. Register it in Application

# app/config/config.neon

extensions:
    - FileSystem\DI\FileSystemExtension

That's it!

Try it Out

Now try using FileSystem\FileSystem in HomepagePresenter:

# app/presenters/HomepagePresenter.php

<?php declare(strict_types=1);

namespace App\Presenters;

use FileSystem\FileSystem;

class HomepagePresenter extends BasePresenter
{
    public function __construct(FileSystem $fileSystem)
    {
        $this->fileSystem = $fileSystem;
    }
}

and running application:

Fails? Damn, I can't put this on my blog.

Oh, we need to tell composer about these classes. He doesn't know, where to find it.

{
    "require-dev": {
        "..."
    },
    "autoload": {
        "psr-4": {
            "FileSystem\\": "src/FileSystem"
        }
    }
}

And manually rebuild autoload.php (composer does by default only after composer update):

composer dump-autoload

Refresh and...

...it works!

Phew! That would have been embarrassing.

To Sum Up

Now you know how to do your first extension.

Let me know if you get stuck somewhere. I want this tutorial to be as easy to understand as possible.




Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors. I'd really appreciate it!