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
41 changes: 17 additions & 24 deletions docs/en/writing-migrations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,7 @@ You can add a check constraint to a table using the ``addCheckConstraint()`` met
{
$table = $this->table('products');
$table->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2])
->addCheckConstraint('price_positive', 'price > 0')
->addCheckConstraint('price > 0', ['name' => 'price_positive'])
->save();
}

Expand All @@ -1562,18 +1562,19 @@ You can add a check constraint to a table using the ``addCheckConstraint()`` met
}
}

The first argument is the constraint name, and the second is the SQL expression
that defines the constraint. The expression should evaluate to a boolean value.
The first argument is the SQL expression that defines the constraint. The expression
should evaluate to a boolean value. The second argument is an options array where
you can specify the constraint ``name``.

Using the CheckConstraint Fluent Builder
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Using the CheckConstraint Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For more complex scenarios, you can use the ``checkConstraint()`` method to get
a fluent builder::
For more complex scenarios, you can create a ``CheckConstraint`` object directly::

<?php

use Migrations\BaseMigration;
use Migrations\Db\Table\CheckConstraint;

class MyNewMigration extends BaseMigration
{
Expand All @@ -1586,14 +1587,10 @@ a fluent builder::
$table->addColumn('age', 'integer')
->addColumn('status', 'string', ['limit' => 20])
->addCheckConstraint(
$this->checkConstraint()
->setName('age_valid')
->setExpression('age >= 18 AND age <= 120')
new CheckConstraint('age_valid', 'age >= 18 AND age <= 120')
)
->addCheckConstraint(
$this->checkConstraint()
->setName('status_valid')
->setExpression("status IN ('active', 'inactive', 'pending')")
new CheckConstraint('status_valid', "status IN ('active', 'inactive', 'pending')")
)
->save();
}
Expand All @@ -1602,8 +1599,7 @@ a fluent builder::
Auto-Generated Constraint Names
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you don't specify a constraint name, one will be automatically generated based
on the table name and expression hash::
If you don't specify a constraint name, one will be automatically generated::

<?php

Expand All @@ -1618,11 +1614,8 @@ on the table name and expression hash::
{
$table = $this->table('inventory');
$table->addColumn('quantity', 'integer')
// Name will be auto-generated like 'inventory_chk_a1b2c3d4'
->addCheckConstraint(
$this->checkConstraint()
->setExpression('quantity >= 0')
)
// Name will be auto-generated
->addCheckConstraint('quantity >= 0')
->save();
}
}
Expand All @@ -1647,8 +1640,8 @@ Check constraints can reference multiple columns and use complex SQL expressions
$table->addColumn('start_date', 'date')
->addColumn('end_date', 'date')
->addColumn('discount', 'decimal', ['precision' => 5, 'scale' => 2])
->addCheckConstraint('valid_date_range', 'end_date >= start_date')
->addCheckConstraint('valid_discount', 'discount BETWEEN 0 AND 100')
->addCheckConstraint('end_date >= start_date', ['name' => 'valid_date_range'])
->addCheckConstraint('discount BETWEEN 0 AND 100', ['name' => 'valid_discount'])
->save();
}
}
Expand All @@ -1674,7 +1667,7 @@ You can verify if a check constraint exists using the ``hasCheckConstraint()`` m
if ($exists) {
// do something
} else {
$table->addCheckConstraint('price_positive', 'price > 0')
$table->addCheckConstraint('price > 0', ['name' => 'price_positive'])
->save();
}
}
Expand Down Expand Up @@ -1708,7 +1701,7 @@ constraint name::
public function down(): void
{
$table = $this->table('products');
$table->addCheckConstraint('price_positive', 'price > 0')
$table->addCheckConstraint('price > 0', ['name' => 'price_positive'])
->save();
}
}
Expand Down
65 changes: 65 additions & 0 deletions src/Db/Action/AddCheckConstraint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);

/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/

namespace Migrations\Db\Action;

use Migrations\Db\Table\CheckConstraint;
use Migrations\Db\Table\TableMetadata;

class AddCheckConstraint extends Action
{
/**
* The check constraint to add
*
* @var \Migrations\Db\Table\CheckConstraint
*/
protected CheckConstraint $checkConstraint;

/**
* Constructor
*
* @param \Migrations\Db\Table\TableMetadata $table The table to add the check constraint to
* @param \Migrations\Db\Table\CheckConstraint $checkConstraint The check constraint to add
*/
public function __construct(TableMetadata $table, CheckConstraint $checkConstraint)
{
parent::__construct($table);
$this->checkConstraint = $checkConstraint;
}

/**
* Creates a new AddCheckConstraint object after building the check constraint with
* the passed attributes
*
* @param \Migrations\Db\Table\TableMetadata $table The table object to add the check constraint to
* @param string $expression The check constraint expression (e.g., "age >= 18")
* @param array<string, mixed> $options Options for the check constraint (e.g., 'name')
* @return self
*/
public static function build(
TableMetadata $table,
string $expression,
array $options = [],
): self {
$name = $options['name'] ?? '';

$checkConstraint = new CheckConstraint($name, $expression);

return new AddCheckConstraint($table, $checkConstraint);
}

/**
* Returns the check constraint to be added
*
* @return \Migrations\Db\Table\CheckConstraint
*/
public function getCheckConstraint(): CheckConstraint
{
return $this->checkConstraint;
}
}
43 changes: 43 additions & 0 deletions src/Db/Action/DropCheckConstraint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);

/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/

namespace Migrations\Db\Action;

use Migrations\Db\Table\TableMetadata;

class DropCheckConstraint extends Action
{
/**
* The check constraint name to drop
*
* @var string
*/
protected string $constraintName;

/**
* Constructor
*
* @param \Migrations\Db\Table\TableMetadata $table The table to remove the constraint from
* @param string $constraintName The name of the check constraint to drop
*/
public function __construct(TableMetadata $table, string $constraintName)
{
parent::__construct($table);
$this->constraintName = $constraintName;
}

/**
* Returns the name of the check constraint to drop
*
* @return string
*/
public function getConstraintName(): string
{
return $this->constraintName;
}
}
18 changes: 18 additions & 0 deletions src/Db/Adapter/AbstractAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Exception;
use InvalidArgumentException;
use Migrations\Config\Config;
use Migrations\Db\Action\AddCheckConstraint;
use Migrations\Db\Action\AddColumn;
use Migrations\Db\Action\AddForeignKey;
use Migrations\Db\Action\AddIndex;
Expand All @@ -32,6 +33,7 @@
use Migrations\Db\Action\ChangePrimaryKey;
use Migrations\Db\Action\CreateTrigger;
use Migrations\Db\Action\CreateView;
use Migrations\Db\Action\DropCheckConstraint;
use Migrations\Db\Action\DropForeignKey;
use Migrations\Db\Action\DropIndex;
use Migrations\Db\Action\DropPartition;
Expand Down Expand Up @@ -1850,6 +1852,22 @@ public function executeActions(TableMetadata $table, array $actions): void
));
break;

case $action instanceof AddCheckConstraint:
/** @var \Migrations\Db\Action\AddCheckConstraint $action */
$instructions->merge($this->getAddCheckConstraintInstructions(
$table,
$action->getCheckConstraint(),
));
break;

case $action instanceof DropCheckConstraint:
/** @var \Migrations\Db\Action\DropCheckConstraint $action */
$instructions->merge($this->getDropCheckConstraintInstructions(
$table->getName(),
$action->getConstraintName(),
));
break;

default:
throw new InvalidArgumentException(
sprintf("Don't know how to execute action `%s`", get_class($action)),
Expand Down
13 changes: 11 additions & 2 deletions src/Db/Plan/Plan.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace Migrations\Db\Plan;

use ArrayObject;
use Migrations\Db\Action\AddCheckConstraint;
use Migrations\Db\Action\AddColumn;
use Migrations\Db\Action\AddForeignKey;
use Migrations\Db\Action\AddIndex;
Expand All @@ -19,6 +20,7 @@
use Migrations\Db\Action\CreateTable;
use Migrations\Db\Action\CreateTrigger;
use Migrations\Db\Action\CreateView;
use Migrations\Db\Action\DropCheckConstraint;
use Migrations\Db\Action\DropForeignKey;
use Migrations\Db\Action\DropIndex;
use Migrations\Db\Action\DropPartition;
Expand Down Expand Up @@ -497,15 +499,22 @@ protected function gatherIndexes(array $actions): void
}

/**
* Collects all foreign key creation and drops from the given intent
* Collects all constraint creation and drops from the given intent
*
* This includes foreign keys and check constraints.
*
* @param \Migrations\Db\Action\Action[] $actions The actions to parse
* @return void
*/
protected function gatherConstraints(array $actions): void
{
foreach ($actions as $action) {
if (!($action instanceof AddForeignKey || $action instanceof DropForeignKey)) {
if (
!($action instanceof AddForeignKey)
&& !($action instanceof DropForeignKey)
&& !($action instanceof AddCheckConstraint)
&& !($action instanceof DropCheckConstraint)
) {
continue;
}
$table = $action->getTable();
Expand Down
47 changes: 47 additions & 0 deletions src/Db/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Cake\Collection\Collection;
use Cake\Core\Configure;
use InvalidArgumentException;
use Migrations\Db\Action\AddCheckConstraint;
use Migrations\Db\Action\AddColumn;
use Migrations\Db\Action\AddForeignKey;
use Migrations\Db\Action\AddIndex;
Expand All @@ -21,6 +22,7 @@
use Migrations\Db\Action\CreateTable;
use Migrations\Db\Action\CreateTrigger;
use Migrations\Db\Action\CreateView;
use Migrations\Db\Action\DropCheckConstraint;
use Migrations\Db\Action\DropForeignKey;
use Migrations\Db\Action\DropIndex;
use Migrations\Db\Action\DropPartition;
Expand All @@ -35,6 +37,7 @@
use Migrations\Db\Adapter\MysqlAdapter;
use Migrations\Db\Plan\Intent;
use Migrations\Db\Plan\Plan;
use Migrations\Db\Table\CheckConstraint;
use Migrations\Db\Table\Column;
use Migrations\Db\Table\ForeignKey;
use Migrations\Db\Table\Index;
Expand Down Expand Up @@ -625,6 +628,50 @@ public function hasForeignKey(string|array $columns, ?string $constraint = null)
return $this->getAdapter()->hasForeignKey($this->getName(), $columns, $constraint);
}

/**
* Add a check constraint to a database table.
*
* @param string|\Migrations\Db\Table\CheckConstraint $expression The check constraint expression or object
* @param array<string, mixed> $options Options for the check constraint (e.g., 'name')
* @return $this
*/
public function addCheckConstraint(string|CheckConstraint $expression, array $options = [])
{
if ($expression instanceof CheckConstraint) {
$action = new AddCheckConstraint($this->table, $expression);
} else {
$action = AddCheckConstraint::build($this->table, $expression, $options);
}
$this->actions->addAction($action);

return $this;
}

/**
* Removes the given check constraint from the table.
*
* @param string $constraintName The name of the check constraint to drop
* @return $this
*/
public function dropCheckConstraint(string $constraintName)
{
$action = new DropCheckConstraint($this->table, $constraintName);
$this->actions->addAction($action);

return $this;
}

/**
* Checks to see if a check constraint exists.
*
* @param string $constraintName The name of the check constraint
* @return bool
*/
public function hasCheckConstraint(string $constraintName): bool
{
return $this->getAdapter()->hasCheckConstraint($this->getName(), $constraintName);
}

/**
* Add partitioning to the table.
*
Expand Down
Loading
Loading