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
1 change: 1 addition & 0 deletions CHANGELOG-5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ All notable changes are documented here. The format is based on [Keep a Changelo
### Fixed

- `Phalcon\Db\Adapter\Pdo\Postgresql::describeReferences()` returning wrong column pairs for composite foreign keys. [#17680](https://github.com/phalcon/cphalcon/issues/17680)
- `Phalcon\Mvc\Model::doLowInsert()` not reading back the generated id on MySQL when the identity property is `0`. [#17689](https://github.com/phalcon/cphalcon/issues/17689)
- `Phalcon\Mvc\View\Engine\Volt\Compiler::compile()` rendering serialized blocks for the second child of a layout when `stat` is `false`. [#17674](https://github.com/phalcon/cphalcon/issues/17674)
- `Phalcon\Mvc\View\Engine\Volt\Compiler::compileCall()` silently dropping `{% call %}` blocks instead of throwing an exception. [#17676](https://github.com/phalcon/cphalcon/issues/17676)
- PHP 8.6 deprecation for session handlers without `create_sid()`: `Phalcon\Session\Adapter\AbstractAdapter` and `Phalcon\Session\Adapter\Noop` now implement `SessionIdInterface` and `create_sid()`. [#17687](https://github.com/phalcon/cphalcon/issues/17687)
Expand Down
11 changes: 7 additions & 4 deletions phalcon/Mvc/Model.zep
Original file line number Diff line number Diff line change
Expand Up @@ -4690,7 +4690,7 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
unsetDefaultValues = [],
value = null,
values = [];
bool useExplicitIdentity;
bool useExplicitIdentity, useSequences;

let bindSkip = Column::BIND_SKIP,
manager = <ManagerInterface> this->modelsManager,
Expand Down Expand Up @@ -4880,9 +4880,10 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
/**
* We check if the model have sequences
*/
let sequenceName = null;
let sequenceName = null,
useSequences = (bool) connection->supportSequences();

if connection->supportSequences() {
if useSequences {
if method_exists(this, "getSequenceName") {
let sequenceName = this->{"getSequenceName"}();
} else {
Expand All @@ -4903,10 +4904,12 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface,
* used, so calling lastInsertId() would fail on PostgreSQL because
* currval() requires nextval() to have been called in the session.
* Reuse the value already present on the model in that case.
* Without sequences, lastInsertId() returns the stored id. MySQL
* can replace 0 with a generated id.
*/
fetch value, this->{attributeField};

if value !== null && value !== "" {
if value !== null && value !== "" && (useSequences || value != 0) {
let lastInsertedId = value;
} else {
let lastInsertedId = connection->lastInsertId(sequenceName);
Expand Down
96 changes: 96 additions & 0 deletions tests/database/Mvc/Model/CreateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Phalcon\Tests\Support\Migrations\InvoicesMigration;
use Phalcon\Tests\Support\Models\Invoices;
use Phalcon\Tests\Support\Traits\DiTrait;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;

use function date;
Expand All @@ -37,6 +38,17 @@ public function setUp(): void
(new InvoicesMigration($connection));
}

/**
* @return array<string, array{0: int|string}>
*/
public static function zeroIdentityProvider(): array
{
return [
'int' => [0],
'string' => ['0'],
];
}

/**
* @author Phalcon Team <team@phalcon.io>
* @since 2020-02-01
Expand Down Expand Up @@ -150,6 +162,30 @@ public function testMvcModelCreateAutoIncrementAfterExplicitIdPgsql(): void
$this->assertGreaterThan(77, (int) $second->inv_id);
}

/**
* An explicit identity value is kept as set on the model (same value and
* type) on MySQL and SQLite.
*
* @issue https://github.com/phalcon/cphalcon/issues/17689
* @author Phalcon Team <team@phalcon.io>
* @since 2026-10-22
*/
#[Group('mysql')]
#[Group('sqlite')]
public function testMvcModelCreateWithExplicitIdMysqlSqlite(): void
{
$invoice = new Invoices();
$invoice->inv_id = 77;
$invoice->inv_cst_id = 2;
$invoice->inv_status_flag = 1;
$invoice->inv_title = uniqid('inv-');
$invoice->inv_total = 50.00;
$invoice->inv_created_at = date('Y-m-d H:i:s');

$this->assertNotFalse($invoice->create());
$this->assertSame(77, $invoice->inv_id);
}

/**
* Inserting with an explicit primary key on PostgreSQL must not throw a
* "currval of sequence not yet defined in this session" error.
Expand All @@ -175,4 +211,64 @@ public function testMvcModelCreateWithExplicitIdPgsql(): void
$this->assertNotFalse($result);
$this->assertSame(77, (int) $invoice->inv_id);
}

/**
* On MySQL, an identity left at 0 is replaced by a generated id; the
* model must read it back with lastInsertId() instead of keeping 0.
* Assumes the default sql_mode (no NO_AUTO_VALUE_ON_ZERO).
*
* @issue https://github.com/phalcon/cphalcon/issues/17689
* @author Phalcon Team <team@phalcon.io>
* @since 2026-10-22
*/
#[DataProvider('zeroIdentityProvider')]
#[Group('mysql')]
public function testMvcModelCreateWithZeroIdentityMysql(int|string $zero): void
{
$invoice = new Invoices();
$invoice->inv_id = $zero;
$invoice->inv_cst_id = 2;
$invoice->inv_status_flag = 1;
$invoice->inv_title = uniqid('inv-');
$invoice->inv_total = 50.00;
$invoice->inv_created_at = date('Y-m-d H:i:s');

$this->assertNotFalse($invoice->create());

$stored = Invoices::findFirst();

$this->assertNotNull($stored);
$this->assertGreaterThan(0, (int) $stored->inv_id);
$this->assertEquals($stored->inv_id, $invoice->inv_id);
$this->assertSame((int) $stored->inv_id, (int) $invoice->inv_id);
}

/**
* On SQLite, an identity of 0 is stored as given; the model reads it
* back with lastInsertId(), which returns it as a string.
*
* @issue https://github.com/phalcon/cphalcon/issues/17689
* @author Phalcon Team <team@phalcon.io>
* @since 2026-10-26
*/
#[DataProvider('zeroIdentityProvider')]
#[Group('sqlite')]
public function testMvcModelCreateWithZeroIdentitySqlite(int|string $zero): void
{
$invoice = new Invoices();
$invoice->inv_id = $zero;
$invoice->inv_cst_id = 2;
$invoice->inv_status_flag = 1;
$invoice->inv_title = uniqid('inv-');
$invoice->inv_total = 50.00;
$invoice->inv_created_at = date('Y-m-d H:i:s');

$this->assertNotFalse($invoice->create());

$stored = Invoices::findFirst();

$this->assertNotNull($stored);
$this->assertSame(0, (int) $stored->inv_id);
$this->assertSame('0', $invoice->inv_id);
}
}
Loading