Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce table editor #6660

Merged
merged 4 commits into from
Dec 22, 2024
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
5 changes: 5 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ awareness about deprecated code.

# Upgrade to 4.3

## `Table::__construct()` marked as internal

The `Table::__construct()` method has been marked as internal. Use `Table::editor()` to instantiate an editor and
`TableEditor::create()` to create a table.

## Deprecated `AbstractAsset::getShortestName()`

The `AbstractAsset::getShortestName()` method has been deprecated. Use `AbstractAsset::getName()` instead.
Expand Down
44 changes: 27 additions & 17 deletions src/Schema/AbstractSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,26 @@ public function listTables(): array
continue;
}

$tables[] = new Table(
$tableName,
$this->_getPortableTableColumnList($tableName, $database, $tableColumns),
$this->_getPortableTableIndexesList($indexColumnsByTable[$tableName] ?? [], $tableName),
[],
$this->_getPortableTableForeignKeysList($foreignKeyColumnsByTable[$tableName] ?? []),
$tableOptionsByTable[$tableName] ?? [],
$configuration,
);
$editor = Table::editor()
->setName($tableName)
->setColumns($this->_getPortableTableColumnList($tableName, $database, $tableColumns))
->setIndexes(
$this->_getPortableTableIndexesList($indexColumnsByTable[$tableName] ?? [], $tableName),
);

if (isset($foreignKeyColumnsByTable[$tableName])) {
$editor->setForeignKeyConstraints(
$this->_getPortableTableForeignKeysList($foreignKeyColumnsByTable[$tableName]),
);
}

if (isset($tableOptionsByTable[$tableName])) {
$editor->setOptions($tableOptionsByTable[$tableName]);
}

$tables[] = $editor
->setConfiguration($configuration)
->create();
}

return $tables;
Expand Down Expand Up @@ -328,14 +339,13 @@ public function introspectTable(string $name): Table
throw TableDoesNotExist::new($name);
}

return new Table(
$name,
$columns,
$this->listTableIndexes($name),
[],
$this->listTableForeignKeys($name),
$this->getTableOptions($name),
);
return Table::editor()
->setName($name)
->setColumns($columns)
->setIndexes($this->listTableIndexes($name))
->setForeignKeyConstraints($this->listTableForeignKeys($name))
->setOptions($this->getTableOptions($name))
->create();
}

/**
Expand Down
17 changes: 17 additions & 0 deletions src/Schema/Exception/InvalidTableDefinition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Schema\Exception;

use Doctrine\DBAL\Schema\SchemaException;
use LogicException;

/** @psalm-immutable */
final class InvalidTableDefinition extends LogicException implements SchemaException
{
public static function nameNotSet(): self
{
return new self('Table name is not set.');
}
}
3 changes: 1 addition & 2 deletions src/Schema/Name.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ interface Name
/**
* Returns the string representation of the name.
*
* The consumers of this method should not rely on a specific return value. It should be used only for diagnostic
* purposes.
* If passed to the corresponding parser, the name should be parsed back to an equivalent object.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This contract enables this expression:

->setName($this->getObjectName()->toString())

*/
public function toString(): string;
}
11 changes: 6 additions & 5 deletions src/Schema/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,13 @@ public function createNamespace(string $name): self
*/
public function createTable(string $name): Table
{
$table = new Table($name, [], [], [], [], [], $this->_schemaConfig->toTableConfiguration());
$this->_addTable($table);
$table = Table::editor()
->setName($name)
->setOptions($this->_schemaConfig->getDefaultTableOptions())
->setConfiguration($this->_schemaConfig->toTableConfiguration())
->create();

foreach ($this->_schemaConfig->getDefaultTableOptions() as $option => $value) {
$table->addOption($option, $value);
}
$this->_addTable($table);

return $table;
}
Expand Down
28 changes: 28 additions & 0 deletions src/Schema/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class Table extends AbstractNamedObject
private int $maxIdentifierLength;

/**
* @internal Use {@link Table::editor()} to instantiate an editor and {@link TableEditor::create()}
* to create a table.
*
* @param array<Column> $columns
* @param array<Index> $indexes
* @param array<UniqueConstraint> $uniqueConstraints
Expand Down Expand Up @@ -847,6 +850,31 @@ public function getComment(): ?string
return $this->_options['comment'] ?? null;
}

/**
* Instantiates a new table editor.
*/
public static function editor(): TableEditor
{
return new TableEditor();
}

/**
* Instantiates a new table editor and initializes it with the table's properties.
*/
public function edit(): TableEditor
{
return self::editor()
->setName($this->getObjectName()->toString())
Copy link
Member Author

@morozov morozov Dec 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though getObjectName() can throw NameIsNotInitialized, this isn't a BC break because Table::edit() is a new method not called from anywhere. It will be used to rename the table in 5.0.

->setColumns($this->_columns)
->setIndexes($this->_indexes)
->setUniqueConstraints($this->uniqueConstraints)
->setForeignKeyConstraints($this->_fkConstraints)
->setOptions($this->_options)
->setConfiguration(
new TableConfiguration($this->maxIdentifierLength),
);
}

/**
* @param array<string|int, string> $columns
* @param array<int, string> $flags
Expand Down
2 changes: 1 addition & 1 deletion src/Schema/TableConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/**
* Contains platform-specific parameters used for creating and managing objects scoped to a {@see Table}.
*/
class TableConfiguration
final class TableConfiguration
{
/** @internal The configuration can be only instantiated by a {@see SchemaConfig}. */
public function __construct(private readonly int $maxIdentifierLength)
Expand Down
105 changes: 105 additions & 0 deletions src/Schema/TableEditor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Schema;

use Doctrine\DBAL\Schema\Exception\InvalidTableDefinition;

final class TableEditor
{
private ?string $name = null;

/** @var array<Column> */
private array $columns = [];

/** @var array<Index> */
private array $indexes = [];

/** @var array<UniqueConstraint> */
private array $uniqueConstraints = [];

/** @var array<ForeignKeyConstraint> */
private array $foreignKeyConstraints = [];

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

private ?TableConfiguration $configuration = null;

/** @internal Use {@link Table::editor()} or {@link Table::edit()} to create an instance */
public function __construct()
{
}

public function setName(string $name): self
{
$this->name = $name;

return $this;
}

/** @param array<Column> $columns */
public function setColumns(array $columns): self
{
$this->columns = $columns;

return $this;
}

/** @param array<Index> $indexes */
public function setIndexes(array $indexes): self
{
$this->indexes = $indexes;

return $this;
}

/** @param array<UniqueConstraint> $uniqueConstraints */
public function setUniqueConstraints(array $uniqueConstraints): self
{
$this->uniqueConstraints = $uniqueConstraints;

return $this;
}

/** @param array<ForeignKeyConstraint> $foreignKeyConstraints */
public function setForeignKeyConstraints(array $foreignKeyConstraints): self
{
$this->foreignKeyConstraints = $foreignKeyConstraints;

return $this;
}

/** @param array<string, mixed> $options */
public function setOptions(array $options): self
{
$this->options = $options;

return $this;
}

public function setConfiguration(TableConfiguration $configuration): self
{
$this->configuration = $configuration;

return $this;
}

public function create(): Table
{
if ($this->name === null) {
throw InvalidTableDefinition::nameNotSet();
}

return new Table(
$this->name,
$this->columns,
$this->indexes,
$this->uniqueConstraints,
$this->foreignKeyConstraints,
$this->options,
$this->configuration,
);
}
}
29 changes: 29 additions & 0 deletions tests/Schema/TableEditorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Tests\Schema;

use Doctrine\DBAL\Schema\Exception\InvalidTableDefinition;
use Doctrine\DBAL\Schema\Table;
use PHPUnit\Framework\TestCase;

class TableEditorTest extends TestCase
{
public function testSetName(): void
{
$table = (new Table('accounts'))
->edit()
->setName('contacts')
->create();

self::assertSame('contacts', $table->getName());
}

public function testNameNotSet(): void
{
$this->expectException(InvalidTableDefinition::class);

Table::editor()->create();
}
}