Skip to content

Model

A model is a representation of your code, that you can create via the fluent api.

There are different types of models available which are explained in this section.

Structured Generateable Models

Structured models are composites and can contain other models. Structured models implement the GenerateableInterface and can be passed to a generator. These are:

  • PhpClass representing a class, with all its methods, properties, comments etc.
  • PhpTrait representing a trait with all its methods, properties etc.
  • PhpInterface representing an interface with all its method signatures, property definitions atc.

Part Models

Structured models can be composed of various members. Functions and methods can itself contain zero to many parameters. All parts are:

  • PhpConstant representing a constant
  • PhpProperty representing a property
  • PhpMethod representing a method
  • PhpParameter representing a function or method parameter

Name vs. Namespace vs. Qualified Name ?

There can be a little struggle about the different names, which are name, namespace and qualified name. Every model has a name and generateable models additionally have a namespace and qualified name. The qualified name is a combination of namespace + name. Here is an overview:

Name Tool
Namespace my\cool
Qualified Name my\cool\Tool

Create your first Class

Let's start with a simple example:

<?php
use Susina\Codegen\Model\PhpClass;

$class = new PhpClass();
$class->setQualifiedName('my\\cool\\Tool');

which will generate an empty class:

<?php
namespace my\cool;

class Tool
{
}

Adding a Constructor

It's better to have a constructor, so we add one:

<?php
use Susina\Codegen\Model\PhpClass;
use Susina\Codegen\Model\PhpMethod;
use Susina\Codegen\Model\PhpParameter;

// You can pass the name or the qualified name when you instantiate your model
$class = new PhpClass('my\\cool\\Tool');
$class
    ->setMethod(PhpMethod::create('__construct')
    ->addParameter(PhpParameter::create('target')
        ->setType('string')
        ->setDescription('Creates my Tool')
        )
    )
;

you can see the fluent API in action and the snippet above will generate:

<?php
namespace my\cool;

class Tool
{

    /**
     * @param string $target Creates my Tool
     */
    public function __construct(string $target)
    {
    }
}

Adding members

We've just learned how to pass a blank method, the constructor to the class. We can also add properties, constants and of course methods. Let's do so:

<?php
use Susina\Codegen\Model\PhpClass;
use Susina\Codegen\Model\PhpMethod;
use Susina\Codegen\Model\PhpParameter;
use Susina\Codegen\Model\PhpProperty;
use Susina\Codegen\Model\PhpConstant;

$class = PhpClass::create('my\\cool\\Tool')
    ->setMethod(PhpMethod::create('setDriver')
        ->addParameter(PhpParameter::create('driver')
            ->setType('string')
        )
        ->setType('bool')  // optional if you want return type
        ->setBody("\$this->driver = \$driver;
return true;"
            )
    )
    ->setProperty(PhpProperty::create('driver')
        ->setVisibility('private')
        ->setType('string')
    )
    ->setConstant(new PhpConstant('FOO', 'bar'))
;

will generate:

<?php
namespace my\cool;

class Tool {

    /**
     */
    const FOO = 'bar';

    /**
     * @var string
     */
    private $driver;

    /**
     *
     * @param string $driver
     * @return bool
     */
    public function setDriver(string $driver): bool
    {
        $this->driver = $driver;
        return true;
    }
}

Let's add some docblock comments, too:

<?php
use Susina\Codegen\Model\PhpClass;
use Susina\Codegen\Model\PhpMethod;
use Susina\Codegen\Model\PhpParameter;
use Susina\Codegen\Model\PhpProperty;
use Susina\Codegen\Model\PhpConstant;

$class = PhpClass::create('my\\cool\\Tool')
    ->setMultilineDescription(["The fantastic Tool class.", "", "@author John Smith"])
    ->setMethod(PhpMethod::create('setDriver')
        ->setDescription("Set the specific driver")
        ->addParameter(PhpParameter::create('driver')
            ->setType('string')
            ->setDescription("The driver")
        )
        ->setType('bool')
        ->setTypeDescription("If everything is ok")
        ->setBody("\$this->driver = \$driver;
return true;"
            )
    )
    ->setProperty(PhpProperty::create('driver')
        ->setVisibility('private')
        ->setType('string')
        ->setDescription("The driver")
    )
    ->setConstant((new PhpConstant('FOO', 'bar'))
        ->setDescription("The FOO constant")
    )
;

will generate:

<?php
namespace my\cool;

/**
* The fantastic Tool class
 * 
 * @author John Smith
 */
class Tool {

    /**
     * The FOO constant 
     */
    const FOO = 'bar';

    /**
     * The driver
     * 
     * @var string
     */
    private $driver;

    /**
     * Set the specific driver
     * 
     * @param string $driver
     * @return bool If everything is ok
     */
    public function setDriver(string $driver): bool
    {
        $this->driver = $driver;
        return true;
    }
}

Declare use statements

When you put code inside a method there can be a reference to a class or interface, where you normally put the qualified name into a use statement. So here is how you do it:

<?php
use Susina\Codegen\Model\PhpClass;
use Susina\Codegen\Model\PhpMethod;

$class = new PhpClass();
$class
    ->setName('Tool')
    ->setNamespace('my\\cool')
    ->setMethod(PhpMethod::create('__construct')
        ->setBody('$request = Request::createFromGlobals();')
    )
    ->declareUse('Symfony\\Component\\HttpFoundation\\Request')
;

which will create:

<?php
namespace my\cool;

use Symfony\Component\HttpFoundation\Request;

class Tool {

    /**
     */
    public function __construct()
    {
        $request = Request::createFromGlobals();
    }
}

Understanding Values

The models PhpConstant, PhpParameter and PhpProperty support values; all of them implement the ValueInterface. Each value object has a type, a value (of course) or an expression. There is a difference between values and expressions. Values refer to language primitives (string, int, float, bool and null). Additionally you can set a PhpConstant as value (the lib understands this as a library primitive ;-). If you want more complex control over the output, you can set an expression instead, which will be generated as is.

Some Examples:

<?php
PhpProperty::create('foo')->setValue('hello world.');
// $foo = 'hello world.';

PhpProperty::create('foo')->setValue(300);
// $foo = 300;

PhpProperty::create('foo')->setValue(3.14);
// $foo = 3.14;

PhpProperty::create('foo')->setValue(false);
// $foo = false;

PhpProperty::create('foo')->setValue(null);
// $foo = null;

PhpProperty::create('foo')->setValue(PhpConstant::create('BAR'));
// $foo = BAR;

PhpProperty::create('foo')->setExpression('self::MY_CONST');
// $foo = self::MY_CONST;

PhpProperty::create('foo')->setExpression("['my' => 'array']");
// $foo = ['my' => 'array'];

For retrieving values there is a hasValue() method which returns true whether there is a value or an expression present. To be sure what is present there is also an isExpression() method which you can use as a second check:

<?php

if ($prop->hasValue()) {
    if ($prop->isExpression()) {
        // do something with an expression
    } else {
        // do something with a value
    }
}

More about types

Returning $this for fluent interface

When you write a class with fluent interface, you want to specify that a method returns $this or the class itself. You can pass the docblock notation to the setType method. I.e.

<?php

$method = new PhpMethod('setDriver');
$method
    ->setType('$this|FileManager')
    ->setTypeDescription('For fluent interface')
;

and it'll result:

<?php

............

/**
 * @return $this|FileManager For fluent interface
 */
public function setDriver(): FileManager
{}

If your method is part of a class, the type of $this is auto-discovered:

<?php

$class = new PhpClass('FileManager');
$method = PhpMethod::create('setDriver')
    ->setType('$this')
    ->setTypeDescription('For fluent interface')
;

$class->setMethod($method);

It'll result:

<?php

...............

class FileManager
{
    /**
     * @return $this|FileManager For fluent interface
     */
    public function setDriver(): FileManager
    {}
}

Nullable types

When you want to define a nullable type, you can use both docblock notation (i.e. int|null) and Php one (i.e. ?int):

<?php

// Docblock notation
$method = PhpMethod::create('fileSize')->setType('int|null');

// or PHP notation
$method1 = PhpMethod::create('fileDescription')->setType('?string');

The result is:

<?php

...................

/**
 * @return int|null
 */
public function fileSize(): ?int
{}

/**
 * @return string|null
 */
public function fileDescription(): ?string
{}

Much, much more

The API has a lot more to offer and has almost full support for what you would expect to manipulate on each model, of course everything is fluent API.