docs/model.md
# 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
<?php
use Susina\Codegen\Model\PhpClass;
$class = new PhpClass();
$class->setQualifiedName('my\\cool\\Tool');
```
which will generate an empty class:
```php
<?php
namespace my\cool;
class Tool
{
}
```
## Adding a Constructor
It's better to have a constructor, so we add one:
```php
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?php
$method = new PhpMethod('setDriver');
$method
->setType('$this|FileManager')
->setTypeDescription('For fluent interface')
;
```
and it'll result:
```php
<?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
<?php
$class = new PhpClass('FileManager');
$method = PhpMethod::create('setDriver')
->setType('$this')
->setTypeDescription('For fluent interface')
;
$class->setMethod($method);
```
It'll result:
```php
<?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
<?php
// Docblock notation
$method = PhpMethod::create('fileSize')->setType('int|null');
// or PHP notation
$method1 = PhpMethod::create('fileDescription')->setType('?string');
```
The result is:
```php
<?php
...................
/**
* @return int|null
*/
public function fileSize(): ?int
{}
/**
* @return string|null
*/
public function fileDescription(): ?string
{}
```
## Much, much more
The [API](api/index.html) 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.