Skip to content

Commit

Permalink
Use ActiveRecord's SchemaBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
olvlvl committed Mar 16, 2023
1 parent 75d2e82 commit 8366e98
Show file tree
Hide file tree
Showing 24 changed files with 144 additions and 328 deletions.
4 changes: 2 additions & 2 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

### New Requirements

None
- PHP 8.1+

### New features

- Added a `ConfigBuild`, to follow ICanBoogie/Config changes.
- Added a `ConfigBuilder`, to follow ICanBoogie/Config changes. Use ActiveRecord's `ConfigBuilder`.
- Added the console commands `activerecord:connections:list` and `activerecord:models:list`, with aliases `activerecord:connections` and `activerecord:models` respectively.
- Configures `StaticModelResolver`.

Expand Down
31 changes: 11 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ connections and models, as well as getters for the connection provider and the m
<?php
namespace ICanBoogie\Binding\ActiveRecord;

use ICanBoogie\ActiveRecord\Model;use ICanBoogie\Application;
use ICanBoogie\Application;
use ICanBoogie\ActiveRecord\Config;
use ICanBoogie\ActiveRecord\Model;
use ICanBoogie\ActiveRecord\ConnectionProvider;
use ICanBoogie\ActiveRecord\ModelProvider;

Expand Down Expand Up @@ -47,19 +49,7 @@ composer require icanboogie/bind-activerecord

## Autoconfig

[ICanBoogie][]'s _Autoconfig_ is used to provide the following features:

- A config builder for the `activerecord` config, created from the `activerecord` fragments.
- A synthesizer for the `activerecord_models` config, created from the `activerecord#models`
fragments.
- A lazy getter for the `ICanBoogie\Application::$connections` property, that returns
a `ConnectionProvider`.
- A lazy getter for the `ICanBoogie\Application::$models` property, that returns
a `ModelProvider`.
- A lazy getter for the `ICanBoogie\Application::$db` property, that returns the connection named
`primary` from the `ICanBoogie\Application::$connections` property.


The package provides a configuration builder for `ICanBoogie\ActiveRecord\Config`.



Expand All @@ -81,8 +71,9 @@ use ICanBoogie\ActiveRecord\ConnectionOptions;
use ICanBoogie\ActiveRecord\Model;
use ICanBoogie\ActiveRecord\Schema;
use ICanBoogie\ActiveRecord\SchemaColumn;
use ICanBoogie\Binding\ActiveRecord\Config;
use ICanBoogie\Binding\ActiveRecord\ConfigBuilder;
use ICanBoogie\ActiveRecord\Config;
use ICanBoogie\ActiveRecord\ConfigBuilder;
use ICanBoogie\ActiveRecord\SchemaBuilder;

return fn(ConfigBuilder $config) => $config
->add_connection(
Expand All @@ -99,10 +90,10 @@ return fn(ConfigBuilder $config) => $config
)
->add_model(
id: 'nodes',
schema: new Schema([
'id' => SchemaColumn::serial(primary: true),
'title' => SchemaColumn::varchar(),
])
activerecord_class: Node::class,
schema_builder: fn(SchemaBuilder $b) => $b
->add_serial('id',primary: true)
->add_varchar('title')
);
```

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"extra": {
"icanboogie": {
"config-constructor": {
"ICanBoogie\\Binding\\ActiveRecord\\Config": "ICanBoogie\\Binding\\ActiveRecord\\ConfigBuilder"
"ICanBoogie\\ActiveRecord\\Config": "ICanBoogie\\Binding\\ActiveRecord\\ConfigBuilder"
},
"config-path": "config"
}
Expand Down
7 changes: 5 additions & 2 deletions config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ services:
_defaults:
autowire: true

ICanBoogie\Binding\ActiveRecord\Config:
ICanBoogie\ActiveRecord\Config:
factory: [ '@ICanBoogie\ConfigProvider', config_for_class ]
arguments:
- ICanBoogie\Binding\ActiveRecord\Config
- ICanBoogie\ActiveRecord\Config

ICanBoogie\ActiveRecord\ConnectionProvider:
class: ICanBoogie\ActiveRecord\ConnectionCollection
Expand All @@ -19,6 +19,9 @@ services:
alias: ICanBoogie\ActiveRecord\ModelCollection
public: true # required by StaticModelProvider

ICanBoogie\ActiveRecord\ModelIterator:
alias: ICanBoogie\ActiveRecord\ModelCollection

ICanBoogie\ActiveRecord\ModelResolver:
alias: ICanBoogie\ActiveRecord\ModelCollection
public: true # required by StaticModelResolver
Expand Down
41 changes: 0 additions & 41 deletions lib/Config.php

This file was deleted.

117 changes: 43 additions & 74 deletions lib/ConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,35 @@

namespace ICanBoogie\Binding\ActiveRecord;

use Closure;
use ICanBoogie\ActiveRecord;
use ICanBoogie\ActiveRecord\ConnectionOptions;
use ICanBoogie\ActiveRecord\Config;
use ICanBoogie\ActiveRecord\Config\ConnectionDefinition;
use ICanBoogie\ActiveRecord\Model;
use ICanBoogie\ActiveRecord\Schema;
use ICanBoogie\ActiveRecord\Table;
use ICanBoogie\ActiveRecord\Query;
use ICanBoogie\ActiveRecord\SchemaBuilder;
use ICanBoogie\Config\Builder;
use InvalidArgumentException;
use LogicException;

use function array_filter;
use function preg_match;

/**
* @implements Builder<Config>
*/
final class ConfigBuilder implements Builder
{
private const REGEXP_TIMEZONE = '/^[-+]\d{2}:\d{2}$/';

public static function get_fragment_filename(): string
{
return 'activerecord';
}

/**
* @param array<int|string, mixed> $array
*
* @return array<int|string, mixed>
*/
private static function filter_non_null(array $array): array
private readonly ActiveRecord\ConfigBuilder $inner;

public function __construct()
{
return array_filter($array, fn(mixed $v): bool => $v !== null);
$this->inner = new ActiveRecord\ConfigBuilder();
}

/**
* @var array<string, array<ConnectionOptions::*, mixed>>
*/
private array $connections = [];

/**
* @var array<string, array<Model::*, mixed>>
*/
private array $models = [];

public function build(): Config
{
return new Config($this->connections, $this->models);
return $this->inner->build();
}

/**
Expand All @@ -69,67 +51,54 @@ public function add_connection(
string|null $username = null,
string|null $password = null,
string|null $table_name_prefix = null,
string $charset_and_collate = ConnectionOptions::DEFAULT_CHARSET_AND_COLLATE,
string $time_zone = ConnectionOptions::DEFAULT_TIMEZONE,
string $charset_and_collate = ConnectionDefinition::DEFAULT_CHARSET_AND_COLLATE,
string $time_zone = ConnectionDefinition::DEFAULT_TIMEZONE,
): self {
$this->assert_time_zone($time_zone);

$this->connections[$id] = self::filter_non_null([ // @phpstan-ignore-line
'dsn' => $dsn,
'username' => $username,
'password' => $password,
'options' => self::filter_non_null([
ConnectionOptions::ID => $id,
ConnectionOptions::TABLE_NAME_PREFIX => $table_name_prefix,
ConnectionOptions::CHARSET_AND_COLLATE => $charset_and_collate,
ConnectionOptions::TIMEZONE => $time_zone,
])
]);
$this->inner->add_connection(
id: $id,
dsn: $dsn,
username: $username,
password: $password,
table_name_prefix: $table_name_prefix,
charset_and_collate: $charset_and_collate,
time_zone: $time_zone
);

return $this;
}

private function assert_time_zone(string $time_zone): void
{
$pattern = self::REGEXP_TIMEZONE;

if (!preg_match($pattern, $time_zone)) {
throw new InvalidArgumentException("Time zone doesn't match pattern '$pattern': $time_zone");
}
}

/**
* @param (Closure(SchemaBuilder $schema): SchemaBuilder) $schema_builder
* @param class-string<ActiveRecord> $activerecord_class
* @param class-string<Model<int|string, ActiveRecord>>|null $model_class
* @param class-string<Query<ActiveRecord>>|null $query_class
*/
public function add_model(
string $id,
Schema $schema,
Closure $schema_builder,
string $activerecord_class,
string $connection = 'primary',
string $connection = Config::DEFAULT_CONNECTION_ID,
string|null $name = null,
string|null $alias = null,
string|null $extends = null,
string|null $implements = null,
string|null $model_class = null,
string|null $query_class = null,
mixed $belongs_to = null,
mixed $has_many = null,
Closure $association_builder = null,
): self {
if ($activerecord_class === ActiveRecord::class) {
throw new LogicException("\$activerecord_class must be an extension of ICanBoogie\ActiveRecord");
}

$this->models[$id] = self::filter_non_null([ // @phpstan-ignore-line
Table::SCHEMA => $schema,
Table::CONNECTION => $connection,
Table::NAME => $name,
Table::ALIAS => $alias,
Table::EXTENDING => $extends,
Table::IMPLEMENTING => $implements,
Model::ID => $id,
Model::ACTIVERECORD_CLASS => $activerecord_class,
Model::CLASSNAME => $model_class,
Model::QUERY_CLASS => $query_class,
Model::BELONGS_TO => $belongs_to,
Model::HAS_MANY => $has_many,
]);
$this->inner->add_model(
id: $id,
schema_builder: $schema_builder,
activerecord_class: $activerecord_class,
connection: $connection,
name: $name,
alias: $alias,
extends: $extends,
implements: $implements,
model_class: $model_class,
query_class: $query_class,
association_builder: $association_builder,
);

return $this;
}
Expand Down
9 changes: 5 additions & 4 deletions lib/Console/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace ICanBoogie\Binding\ActiveRecord\Console;

use ICanBoogie\ActiveRecord\ModelIterator;
use ICanBoogie\ActiveRecord\ModelProvider;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -15,9 +16,9 @@ final class InstallCommand extends Command
protected static $defaultDescription = "Install models";

public function __construct(
private readonly ModelProvider $models
)
{
private readonly ModelProvider $models,
private readonly ModelIterator $iterator,
) {
parent::__construct();
}

Expand Down Expand Up @@ -62,7 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return $tried[$id] = false;
};

foreach ($this->models as $id => $_) {
foreach ($this->iterator->model_iterator() as $id => $_) {
$recursive_install($id);
}

Expand Down
5 changes: 2 additions & 3 deletions lib/Console/ListConnectionsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

namespace ICanBoogie\Binding\ActiveRecord\Console;

use ICanBoogie\ActiveRecord\ConnectionOptions;
use ICanBoogie\Binding\ActiveRecord\Config;
use ICanBoogie\ActiveRecord\Config;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -27,7 +26,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
foreach ($this->config->connections as $id => $attributes) {
$rows[] = [
$id,
$attributes[ConnectionOptions::TABLE_NAME_PREFIX] ?? "",
$attributes->table_name_prefix ?? "",
];
}

Expand Down
5 changes: 2 additions & 3 deletions lib/Console/ListModelsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

namespace ICanBoogie\Binding\ActiveRecord\Console;

use ICanBoogie\ActiveRecord\Model;
use ICanBoogie\Binding\ActiveRecord\Config;
use ICanBoogie\ActiveRecord\Config;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -27,7 +26,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
foreach ($this->config->models as $model_id => $attributes) {
$rows[] = [
$model_id,
$attributes[Model::CONNECTION],
$attributes->connection,
];
}

Expand Down
3 changes: 2 additions & 1 deletion lib/ContainerExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace ICanBoogie\Binding\ActiveRecord;

use ICanBoogie\ActiveRecord\Config;
use ICanBoogie\ActiveRecord\Connection;
use ICanBoogie\ActiveRecord\ConnectionProvider;
use ICanBoogie\ActiveRecord\Model;
Expand Down Expand Up @@ -69,7 +70,7 @@ private function register_connections(ContainerBuilder $container): void
private function register_models(ContainerBuilder $container): void
{
foreach ($this->config->models as $id => $model) {
$class = $model[Model::CLASSNAME] ?? Model::class;
$class = $model->model_class ?? Model::class;

assert(is_string($class));

Expand Down
Loading

0 comments on commit 8366e98

Please sign in to comment.