Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ The base model gives you the methods most applications reach for first:

If your table includes `created_at` and `updated_at`, they are populated automatically on insert and update.

Timestamps are generated in UTC using the `Y-m-d H:i:s` format. SQLite stores those values as text, while MySQL/MariaDB and PostgreSQL accept them in timestamp-style columns.

### Dynamic finders and counters

Build expressive queries straight from method names:
Expand Down Expand Up @@ -157,6 +159,8 @@ $statement = Freshsauce\Model\Model::execute(
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
```

If you change a table schema at runtime and need the model to see the new columns without reconnecting, call `YourModel::refreshTableMetadata()`.

### Validation hooks

Use instance-aware hooks when writes need application rules:
Expand Down Expand Up @@ -226,6 +230,8 @@ Freshsauce\Model\Model::connectDb(

SQLite is supported in the library and covered by the automated test suite.

Schema-qualified table names such as `reporting.categories` are supported for PostgreSQL models.

## Built for real projects

The repository includes:
Expand Down
56 changes: 48 additions & 8 deletions src/Model/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,16 @@ protected static function getFieldnames(): array
return self::$_tableColumns[$class];
}

/**
* Refresh cached table metadata for the current model class.
*
* @return void
*/
public static function refreshTableMetadata(): void
{
unset(self::$_tableColumns[static::class]);
}

/**
* Split a table name into schema and table, defaulting schema to public.
*
Expand Down Expand Up @@ -985,7 +995,7 @@ protected static function fetchWhereWithSuffix(string $SQLfragment = '', array $
* returns an array of objects of the sub-class which match the conditions
*
* @param string $SQLfragment conditions, sorting, grouping and limit to apply (to right of WHERE keywords)
* @param array<int, mixed> $params optional params to be escaped and injected into the SQL query (standrd PDO syntax)
* @param array<int, mixed> $params optional params to be escaped and injected into the SQL query (standard PDO syntax)
* @param bool $limitOne if true the first match will be returned
*
* @return array|static|null
Expand All @@ -999,7 +1009,7 @@ public static function fetchWhere(string $SQLfragment = '', array $params = [],
* returns an array of objects of the sub-class which match the conditions
*
* @param string $SQLfragment conditions, sorting, grouping and limit to apply (to right of WHERE keywords)
* @param array $params optional params to be escaped and injected into the SQL query (standrd PDO syntax)
* @param array $params optional params to be escaped and injected into the SQL query (standard PDO syntax)
*
* @return array object[] of objects of calling class
*/
Expand All @@ -1014,7 +1024,7 @@ public static function fetchAllWhere(string $SQLfragment = '', array $params = [
* returns an object of the sub-class which matches the conditions
*
* @param string $SQLfragment conditions, sorting, grouping and limit to apply (to right of WHERE keywords)
* @param array $params optional params to be escaped and injected into the SQL query (standrd PDO syntax)
* @param array $params optional params to be escaped and injected into the SQL query (standard PDO syntax)
*
* @return static|null object of calling class
*/
Expand Down Expand Up @@ -1137,7 +1147,7 @@ public function delete()
* Delete records based on an SQL conditions
*
* @param string $where SQL fragment of conditions
* @param array $params optional params to be escaped and injected into the SQL query (standrd PDO syntax)
* @param array $params optional params to be escaped and injected into the SQL query (standard PDO syntax)
*
* @return \PDOStatement
*/
Expand Down Expand Up @@ -1206,6 +1216,16 @@ protected function runUpdateValidation(): void
$this->validateForUpdate();
}

/**
* Return a UTC timestamp string suitable for the built-in timestamp columns.
*
* @return string
*/
protected static function currentTimestamp(): string
{
return gmdate('Y-m-d H:i:s');
}

/**
* insert a row into the database table, and update the primary key field with the one generated on insert
*
Expand All @@ -1219,7 +1239,7 @@ protected function runUpdateValidation(): void
public function insert(bool $autoTimestamp = true, bool $allowSetPrimaryKey = false): bool
{
$pk = static::$_primary_column_name;
$timeStr = gmdate('Y-m-d H:i:s');
$timeStr = static::currentTimestamp();
if ($autoTimestamp && in_array('created_at', static::getFieldnames())) {
$this->created_at = $timeStr;
}
Expand Down Expand Up @@ -1291,7 +1311,7 @@ public function insert(bool $autoTimestamp = true, bool $allowSetPrimaryKey = fa
public function update(bool $autoTimestamp = true): bool
{
if ($autoTimestamp && in_array('updated_at', static::getFieldnames())) {
$this->updated_at = gmdate('Y-m-d H:i:s');
$this->updated_at = static::currentTimestamp();
}
$this->runUpdateValidation();
$set = $this->setString();
Expand All @@ -1305,10 +1325,11 @@ public function update(bool $autoTimestamp = true): bool
$query,
$set['params']
);
if ($st->rowCount() == 1) {
if ($this->updateSucceeded($st)) {
$this->clearDirtyFields();
return true;
}
return ($st->rowCount() == 1);
return false;
}

/**
Expand Down Expand Up @@ -1417,6 +1438,25 @@ protected function hasPrimaryKeyValue(): bool
return $this->$primaryKey !== null;
}

/**
* Determine whether an update succeeded even when the driver reports zero changed rows.
*
* @param \PDOStatement $statement
*
* @return bool
*/
protected function updateSucceeded(\PDOStatement $statement): bool
{
if ($statement->rowCount() > 0) {
return true;
}

return static::existsWhere(
static::_quote_identifier(static::$_primary_column_name) . ' = ?',
[$this->{static::$_primary_column_name}]
Comment thread
davebarnwell marked this conversation as resolved.
Outdated
);
}

/**
* @param mixed $match
*
Expand Down
16 changes: 16 additions & 0 deletions test-src/Model/CodedCategory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Model;

/**
* @property int|null $code
* @property string|null $name
*/
class CodedCategory extends \Freshsauce\Model\Model
{
protected static $_primary_column_name = 'code';

protected static $_tableName = 'coded_categories';
}
15 changes: 15 additions & 0 deletions test-src/Model/MetadataRefreshCategory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\Model;

/**
* @property int|null $id
* @property string|null $name
* @property string|null $description
*/
class MetadataRefreshCategory extends \Freshsauce\Model\Model
{
protected static $_tableName = 'metadata_refresh_categories';
}
14 changes: 14 additions & 0 deletions test-src/Model/SchemaQualifiedCategory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace App\Model;

/**
* @property int|null $id
* @property string|null $name
*/
class SchemaQualifiedCategory extends \Freshsauce\Model\Model
{
protected static $_tableName = 'orm_phase3.schema_categories';
}
14 changes: 14 additions & 0 deletions test-src/Model/UntimedCategory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace App\Model;

/**
* @property int|null $id
* @property string|null $name
*/
class UntimedCategory extends \Freshsauce\Model\Model
{
protected static $_tableName = 'untimed_categories';
}
Loading