Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ All notable changes are documented here. The format is based on [Keep a Changelo
- Added `Crest\Generator\Project`: `Skeleton` (the files, the values and the first artifacts of the project of a flavor), `AdrSkeleton`, `MvcSkeleton`, `Skeletons`, `Answers` and `TemplateEngines`, and `Crest\Generator\ControllerBase`. [#47](https://github.com/phalcon/crest/issues/47)
- Added the flavor question of `init`, and `init --flavor`: `init` proposes `adr` or `mvc` from the default folders of each flavor that the project has, and asks for it after the namespace. A tie gives `adr`. [#48](https://github.com/phalcon/crest/issues/48)
- Added `Crest\Project\Config::NO_CLASS_PATHS` (`views`), the path keys of folders with no classes, and `Crest\Project\Survey::flavor()`. [#48](https://github.com/phalcon/crest/issues/48)
- Added `make:model` for MVC projects: it reads a table through the `db` service of the project (crest boots the front controller that `bootstrap` names), and writes a model in `src/Models/` and its base in `src/Models/Base/`, which crest owns. The base has a typed nullable property for each column and `initialize()` with `setSource()`. A second run writes the base again from the table and keeps the model. [#50](https://github.com/phalcon/crest/issues/50)
- Added the `model` default path (`src/Models`) for MVC projects, and the MVC stubs `model`, `model-base` and `fragment-model-property`. [#50](https://github.com/phalcon/crest/issues/50)
- Added `Crest\Generator\ClassName::fromTable()` and `RESERVED`, `Crest\Generator\ModelProperties` and `Crest\Project\Database`. `ClassName::PATTERN` is public. [#50](https://github.com/phalcon/crest/issues/50)

### Changed

Expand Down Expand Up @@ -108,6 +111,7 @@ All notable changes are documented here. The format is based on [Keep a Changelo
- Changed `init`: it asks one more question, the flavor, after the namespace, so a script that sends answers to `init` needs one more line. For an MVC project, it asks for the MVC paths. `views` is a folder in the project, with no psr-4 check; its proposal is the only folder in `themes/` when `themes/default` does not exist. [#48](https://github.com/phalcon/crest/issues/48)
- Changed `Crest\Project\Survey::paths()` and `settings()`: `paths()` takes the root, the folder and the flavor; `settings()` takes the root and the flavors to compare. [#48](https://github.com/phalcon/crest/issues/48)
- Changed `make:provider`: it also runs in MVC projects. There, it writes a provider that implements `Phalcon\Di\ServiceProviderInterface` into the `provider` path (`src/Providers`), and prints the line to add to `providers()` of the front controller. New MVC stubs: `provider` and `fragment-guidance-provider`. [#49](https://github.com/phalcon/crest/issues/49)
- Changed `init`: for an MVC project, it also asks for the `model` folder, so a script that sends answers to `init` needs one more line. [#50](https://github.com/phalcon/crest/issues/50)

### Fixed

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"pds/skeleton": "^1",
"phalcon/code-quality": "^1.0",
"phalcon/phalcon": "v6.0.x-dev",
"phalcon/phql": "1.0.x-dev",
"phalcon/talon": "^1.0.0",
"phpstan/phpstan": "^2",
"phpunit/phpunit": "^10.5",
Expand Down
83 changes: 81 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

100 changes: 87 additions & 13 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,20 @@ the commands for the flavor of the project.
| `make:command` | create a crest command |
| `make:controller` | create an MVC controller |
| `make:middleware` | create an ADR middleware |
| `make:model` | create an MVC model from a database table |
| `make:provider` | create a service provider |
| `make:responder` | create an ADR responder |
| `new` | create an ADR project |
| `new` | create an ADR or MVC project |
| `route:list` | every route the application answers |
| `serve` (`server`) | start PHP's built-in web server for the project |
| `stub:publish` | copy packaged stubs into the project for editing |
| `up` | start the project containers |

`make:command` works in each flavor. `make:provider` works in ADR and MVC projects,
with a stub for each flavor. `make:controller` is for MVC projects only. These
commands are for ADR projects only: `make:action`, `make:middleware`,
`make:responder`, `route:list`, `container:list` and `event:list`. In a project of
a different flavor, they stop with an error:
with a stub for each flavor. `make:controller` and `make:model` are for MVC
projects only. These commands are for ADR projects only: `make:action`,
`make:middleware`, `make:responder`, `route:list`, `container:list` and
`event:list`. In a project of a different flavor, they stop with an error:

crest: make:responder does not support the 'mvc' flavor of this project; it supports: adr

Expand Down Expand Up @@ -227,9 +228,9 @@ applications (vokuro, invo):
| `src/Controllers/Base/` | `CrestControllerBase`, the base that crest owns (see "Generated bases") |
| `themes/default/` | the layout `index.phtml` and the view `index/index.phtml` |

`crest.php` has `'flavor' => 'mvc'` and the paths `controller`, `provider` and
`views`. To add a service, run `crest make:provider Mailer`. It writes
`src/Providers/MailerProvider.php`, a provider that implements
`crest.php` has `'flavor' => 'mvc'` and the paths `controller`, `model`,
`provider` and `views`. To add a service, run `crest make:provider Mailer`. It
writes `src/Providers/MailerProvider.php`, a provider that implements
`Phalcon\Di\ServiceProviderInterface`, and prints the line to add to
`AppFront::providers()`.

Expand Down Expand Up @@ -333,9 +334,9 @@ namespaces from it. Without one, `init` stops and tells you to add one, for exam

## Commands that boot the project

`container:list` and `event:list` read state that exists only once the
application has registered it, so they start your front controller. Name it in
`crest.php`:
`container:list`, `event:list` and `make:model` read state that exists only
once the application has registered it, so they start your front controller.
Name it in `crest.php`:

```php
return [
Expand Down Expand Up @@ -374,6 +375,7 @@ the command:
|---|---|
| `container:list` | implement `Phalcon\Contracts\Container\Service\Collection` and `Enumerable` |
| `event:list` | implement `Collection`, and hold a registered `Phalcon\Events\Manager` |
| `make:model` | implement `Phalcon\Di\DiInterface`, and hold a `db` service that is a `Phalcon\Db\Adapter\AdapterInterface` |

`Phalcon\Container\Container` implements both contracts, so returning one is
enough for `container:list`.
Expand All @@ -383,8 +385,9 @@ manager the container would merely autowire does not count - crest reports that
the bootstrap registers none, rather than listing zero listeners off a fresh
instance it created itself.

Everything else - the generators, `about`, `config:show` and `route:list` -
reads the filesystem and keeps working on a project that does not currently run.
Everything else - the other generators, `about`, `config:show` and
`route:list` - reads the filesystem and keeps working on a project that does
not currently run.

## Generating actions

Expand Down Expand Up @@ -458,6 +461,77 @@ each one only once.
line in your copy, so that crest writes the base again when you change the
stub.

## Generating models

In an MVC project, `make:model` reads a table from the database of the project,
and writes a model and the base class that it extends:

crest make:model users

Created src/Models/Base/UsersBase.php
Created src/Models/Users.php

Crest reads the table through the `db` service of your project, so it starts
your front controller: `crest.php` must name it in `bootstrap` (see "Commands
that boot the project"). A project from `crest new --flavor=mvc` names
`AppFront`, but it has no `db` service yet. Add a provider that registers one
(`crest make:provider Db`).

With the `phalcon/phalcon` package (Phalcon v6), a model also needs
`phalcon/phql`, because a model uses PHQL to find rows. The extension (Phalcon
v5) has PHQL. Install the package in your project:

composer require phalcon/phql:1.0.x-dev

`Users` extends `UsersBase`, which extends `Phalcon\Mvc\Model`. The base has a
property for each column, in the order of the table, and an `initialize()` that
sets the table:

```php
public ?int $id = null;
public ?string $name = null;
```

| Column type | Property type |
|---|---|
| the integer types | `?int` |
| `FLOAT`, `DOUBLE` | `?float` |
| `BOOLEAN` | `?bool` |
| all others, also `DECIMAL`, the dates and text | `?string` |

The column type is the type that Phalcon reads from the database. On SQLite,
Phalcon reads `DOUBLE` and `BOOLEAN` columns as `VARCHAR`, so they give
`?string`.

Each property is nullable, with the default `null`, because a new model has no
values yet. The class name comes from the table name: crest makes the first
letter of each part between `_` uppercase. `user_profiles` gives
`UserProfiles`.

When the table changes, run `make:model` again. Crest writes the base again,
and keeps your model:

Updated src/Models/Base/UsersBase.php
Kept src/Models/Users.php: it exists; pass --force to replace it

The rules of "Generated bases" apply to the base of a model too: do not edit
it, and `--force` replaces the model, but never a base without the
`Generated by crest.` line. If you add `initialize()` to your model, call
`parent::initialize()` first.

Crest stops, and writes no file, when:

- the table name gives no class name (`user-profiles`), or a name that PHP
reserves (`list`);
- the project has no `db` service, the service is not a Phalcon database
adapter, or it cannot connect;
- the table does not exist;
- a column name is not a property name (`first name`), or it is the name of a
property of `Phalcon\Mvc\Model` (`container`).

`make:model` is for MVC projects. An ADR project uses repositories, not Active
Record models, so the command stops there.

## Adding commands from a package

A package adds commands to crest in its `composer.json`:
Expand Down
1 change: 1 addition & 0 deletions resources/stubs/mvc/fragment-model-property.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public ?{{ type }} ${{ column }} = null;
20 changes: 20 additions & 0 deletions resources/stubs/mvc/model-base.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace {{ namespace }};

use Phalcon\Mvc\Model;

/**
* Generated by crest. Do not edit this file: crest writes it again when you
* run make:model {{ table }}. Put your code into the model that extends it.
*/
abstract class {{ class }} extends Model
{
{{ properties }}
public function initialize(): void
{
$this->setSource('{{ table }}');
}
}
12 changes: 12 additions & 0 deletions resources/stubs/mvc/model.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace {{ namespace }};

use {{ baseNamespace }}\{{ base }};

final class {{ class }} extends {{ base }}
{
// If you add initialize(), call parent::initialize() first.
}
26 changes: 1 addition & 25 deletions src/Command/Make/ControllerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Crest\Console\Exceptions\Exception;
use Crest\Console\Input;
use Crest\Console\Output;
use Crest\Generator\ArtifactWriter;
use Crest\Generator\ControllerBase;
use Crest\Generator\Outcome;
use Crest\Project\Flavor;
Expand Down Expand Up @@ -67,7 +66,7 @@ public function handle(Input $input, Output $output): int
$writer = $this->writer($config);

// The base first: a controller never exists without its base.
$this->reportBase(
$this->reportOwned(
$output,
$writer->renderOwned(
$base,
Expand Down Expand Up @@ -127,29 +126,6 @@ private function camelize(string $route): string
return implode('', array_map('ucfirst', preg_split('/[_-]+/', $route) ?: []));
}

/**
* One line for the base: none when it did not change, and a note when
* crest kept it.
*/
private function reportBase(Output $output, Outcome $outcome, string $file): void
{
if (Outcome::Kept === $outcome) {
$output->line(
sprintf(
'Kept %s: it has no "%s" line, so crest does not change it',
$file,
ArtifactWriter::MARKER
)
);

return;
}

if (Outcome::Unchanged !== $outcome) {
$output->success(sprintf('%s %s', Outcome::Created === $outcome ? 'Created' : 'Updated', $file));
}
}

/**
* The URL segment of the controller with the default routes, after two
* checks of the name. Both stop the command before it writes a file.
Expand Down
Loading
Loading