nette / php-generator

🐘 Generates neat PHP code for you. Supports new PHP 8.3 features.
https://doc.nette.org/php-generator
Other
2.11k stars 138 forks source link

Force the rendering of `use` #43

Closed na2axl closed 5 years ago

na2axl commented 5 years ago

Hi @nette team,

I am building an ORM (LightQL) for my framework and now I'm creating a CLI tool used to generate PHP entities from a database schema. I'm able to generate the PHP file perfectly through your package but I'm facing a little problem: I'm unable to force the rendering of use.

These use have to be rendered for the entities to work well since even if they are not used in PHP code or even if the class to use believe in the same namespace than the generated one, they are used by annotations in comments and resolved by the LightQL backend (for one-to-one or one-to-many associations for example). Because this goal cannot be achieved now (or maybe I have missed some configurations), generated entities looks very fine but cannot be used without be edited manually...

So I think it could be fine if we have a configuration value used to define if the we want to force the rendering of use (or other objects) or not.

Thanks a lot for this awesome work! :grin:

JanTvrdik commented 5 years ago

Generating use statements should work just fine. See documentation for examples of how to do it.

na2axl commented 5 years ago

Yes @JanTvrdik it works. But my problem is the following:

For example, I have a namespace (MyProject\Entities), I'm generating some classes in this namespace (MyProject\Entities\User, MyProject\Entities\Topic), and since in the database, user and topic tables are in one-to-many relation, LightQL want that there is a use statement (use MyProject\Entities\User) in the Topic class. But this goal cannot be achieved now because in your package you are skipping the rendering of use statements when they are in the same namespace (in my example, Topic class need to use User class, but they live in the same namespace MyProject\Entities so use statement is not rendered).

So I'm not asking to you to suppress this behaviour (it's just normal to skip rendering in these cases) but I just want that you add a way to let the user decide if in these cases the generator forces the rendering.

dg commented 5 years ago

This is special case, so I think best way is to create own printer:

class OwnPrinter extends Nette\PhpGenerator\Printer
{
    public function printNamespace(Nette\PhpGenerator\PhpNamespace $namespace): string
    {
        $name = $namespace->getName();

        $uses = [];
        foreach ($namespace->getUses() as $alias => $original) {
            if ($alias === $original || substr($original, -(strlen($alias) + 1)) === '\\' . $alias) {
                $uses[] = "use $original;";
            } else {
                $uses[] = "use $original as $alias;";
            }
        }

        $classes = [];
        foreach ($namespace->getClasses() as $class) {
            $classes[] = $this->printClass($class, $namespace);
        }

        $body = ($uses ? implode("\n", $uses) . "\n\n" : '')
            . implode("\n", $classes);

        if ($namespace->getBracketedSyntax()) {
            return 'namespace' . ($name ? " $name" : '') . "\n{\n"
                . $this->indent($body)
                . "}\n";

        } else {
            return ($name ? "namespace $name;\n\n" : '')
                . $body;
        }
    }
}

usage:

$printer = new OwnPrinter;
$printer->printNamespace($namespace));
na2axl commented 5 years ago

Thanks @dg your solution solves my problem. I have missed the idea to create my own printer 😄.