Skip to content

Instantly share code, notes, and snippets.

@bazo
Last active October 10, 2018 06:44
Show Gist options
  • Save bazo/358977a9c27d7ed92ef8 to your computer and use it in GitHub Desktop.
Save bazo/358977a9c27d7ed92ef8 to your computer and use it in GitHub Desktop.
<?php
use Nette\Application\UI\Presenter;
use Nette\DI\CompilerExtension;
/**
* Auto register presenters as services
* @author Martin Bažík <martin@bazo.sk>
*/
class PresenterExtension extends CompilerExtension
{
private $defaults = [
'robotLoaderVarName' => 'robotLoader'
];
public function loadConfiguration()
{
$config = $this->getConfig($this->defaults);
$builder = $this->getContainerBuilder();
$robotLoaderVarName = $config['robotLoaderVarName'];
global $$robotLoaderVarName;
$classes = $$robotLoaderVarName->getIndexedClasses();
foreach ($classes as $class => $file) {
$r = new ReflectionClass($class);
if (!$r->isAbstract() and $r->isSubclassOf(Presenter::class)) {
$defName = str_replace('\\', '_', $r->getName());
$builder
->addDefinition($defName)
->setClass($class)
->setAutowired(FALSE)
;
}
}
}
}
@jsifalda
Copy link

global? I thought is key is permitted to use in php ;)

@TomasVotruba
Copy link

I don't like that global $$robotLoaderVarName; either.

Reminds me of ModelLoader. Comfy, but not really clear, esp. for new programmers.

Also this complicates passing parameters to presenters via config.

I'm for use when needed approach, not global solution. It was similar issue, when inject was called globally to all services and now it moves to tag on class you choose.

@fprochazka
Copy link

I've made this for myself.

<?php
class DamejidloExtension extends Nette\DI\CompilerExtension
{

    protected function loadPresenters()
    {
        $builder = $this->getContainerBuilder();

        if ($builder->parameters['debugMode'] || defined('DAMEJIDLO_TESTS_ENV') || PHP_SAPI === 'cli') {
            return; // production only
        }

        $robot = new Nette\Loaders\RobotLoader();
        $robot->addDirectory($builder->expand('%appDir%'));
        $robot->setCacheStorage(new Nette\Caching\Storages\MemoryStorage());
        $robot->rebuild();

        $counter = 0;
        foreach ($robot->getIndexedClasses() as $class => $file) {
            try {
                $refl = Nette\Reflection\ClassType::from($class);

                if (!$refl->implementsInterface(Nette\Application\IPresenter::class)) {
                    continue;
                }

                if (!$refl->isInstantiable()) {
                    continue;
                }

                $builder->addDefinition($this->prefix('presenter.' . (++$counter)))
                    ->setClass($class)
                    ->setInject(TRUE);

            } catch (\ReflectionException $e) {
                continue;
            }
        }

    }

Because I'm not using the robot loader in production, I have a composer classmap generated for the app/ dir.

@bazo
Copy link
Author

bazo commented Aug 30, 2014

oh boys you're so demanding:) it was 5min work together with ide start. global was the fastest way to reuse existing robot loader. filips approach is better ofc.

btw you need to set ->autowired to FALSE otherwise it will fail on components without overrideen constructors. and when inject is on, nette complains about injectPrimary

@fprochazka
Copy link

Fuck, in 2.1 (which I'm using on damejidlo), this line is in the else condition branch.

@fprochazka
Copy link

The whole point of this is to not have dynamic injects, but have them compiled in the container. So I guess that in 2.2, the PresenterFactory must be hacked.

@fprochazka
Copy link

@TomasVotruba
Copy link

@bazo Just pushing the boundaries :) Thanks for starting discussion with some specific code.

The component issue can be also solved via own BaseControl with empty __construct.

Thanks @fprochazka for extension, gonna test it soon.

@fprochazka
Copy link

@TomasVotruba
Copy link

Thanks both. With addExcludedClasses and setAutowired(FALSE) everything seems to work fine.

@TomasVotruba
Copy link

I've got more time for testing now and had problem:

  1. with inject as you wrote Filip
  2. and autowired property in trait. Had to made it public to make it work.

I really don't understand the output of your discussion with David. Could you sum up pls?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment