Skip to content

Commit 822de6c

Browse files
committed
Cast Virtual Field
1 parent 9938e39 commit 822de6c

File tree

7 files changed

+71
-6
lines changed

7 files changed

+71
-6
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ Why another PHP ORM? In writing minimal and fast websites, it was determined tha
77

88
## Features
99
- **Active Records** A fully type checked object interface and implement basic CRUD functionality.
10-
- **Active Tables** Full table operations including support for where, having, limits, ordering, grouping, joins and unions.
10+
- **Active Tables** Full table operations (select, update, insert and delete) including support for where, having, limits, ordering, grouping, joins and unions.
1111
- **Data Cursors** Cursors implement **iterable** and **Countable** eliminating the need for full arrays read into memory.
1212
- **Validation** Fully customizable and translatable backend validation.
13-
- **Virtual Fields** Supports get and set semantics for any custom or calculated field.
13+
- **Virtual Fields** Supports get and set semantics for any custom or calculated field such as Carbon dates.
1414
- **Migrations** Simple migrations offer atomic up and down migrations.
1515
- **Relations** Parent, children, one to one, many to many, and custom relationships.
1616
- **Transactions** Object based transaction meaning exceptions can not leave an open transacton.
17-
- **Type Safe** Prevent stupid type errors.
17+
- **Type Safe** Prevents stupid type errors.
18+
- **Injection Safe** Uses PDO placeholders and field sanitation to prevent injection attacks.
1819
- **Raw SQL Query Support** Execute any valid SQL command.
1920
- **Multiple Database Support** Work with multiple databases simultaneously.
2021
- **Multi-Vendor Support** Built on PDO with support for MySQL, MariaDB and SQLite.
@@ -115,7 +116,7 @@ Exceptions are generated in the following conditions:
115116
All of the above exceptions are programmer errors and strictly enforced. Empty queries are not considered errors. SQL may also return [Exceptions](https://www.php.net/manual/en/class.exception.php) if invalid fields are used.
116117

117118
### Type Conversions
118-
If you set a field to the wrong type, the library logs an warning then converts the type via the appropriate PHP cast.
119+
If you set a field to the wrong type, the library logs a warning then converts the type via the appropriate PHP cast.
119120

120121
## Multiple Database Support
121122
While this is primarily a single database ORM, you can switch databases at run time. Save the value from `$connectionId = \PHPFUI\ORM::addConnection($pdo);` and then call `\PHPFUI\ORM::useConnection($db);` to switch. `\PHPFUI\ORM::addConnection` will set the current connection.

Tests/Fixtures/Record/Order.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@ class Order extends \Tests\Fixtures\Record\Definition\Order
66
{
77
protected static array $virtualFields = [
88
'OrderDetailChildren' => [\PHPFUI\ORM\Children::class, \Tests\Fixtures\Table\OrderDetail::class, 'order_detail_id'],
9+
'order_date' => [\PHPFUI\ORM\Cast::class, \Carbon\Carbon::class],
10+
'paid_date' => [\PHPFUI\ORM\Cast::class, \Carbon\Carbon::class],
11+
'shipped_date' => [\PHPFUI\ORM\Cast::class, \Carbon\Carbon::class],
912
];
1013
}

Tests/Unit/CastTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Tests\Unit;
4+
5+
class CastTest extends \PHPUnit\Framework\TestCase
6+
{
7+
public function testOrderCast() : void
8+
{
9+
$order = new \Tests\Fixtures\Record\Order(30);
10+
$this->assertTrue($order->loaded());
11+
$this->assertEquals(7, $order->order_date->diffInDays($order->shipped_date));
12+
$ship_date = $order->shipped_date->addDays(10);
13+
$order->shipped_date = $ship_date;
14+
$this->assertEquals(17, $order->order_date->diffInDays($order->shipped_date));
15+
}
16+
}

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"phpfui/phpunit-syntax-coverage": "^1.0",
2121
"roave/security-advisories": "dev-latest",
2222
"friendsofphp/php-cs-fixer": "^3.0",
23+
"nesbot/carbon": "*",
2324
"phpstan/phpstan": "^1.8"
2425
},
2526
"autoload": {

docs/5. Virtual Fields.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ You can define virtual fields with get and set semantics for any **\App\Record**
55

66
Every **\App\Record** class has a static $virtualFields array defined. The key of the array is the name of the virtual key. The value for each virtual field is an array of strings. The first string is the virtual field class name. Subsequent parameters are are passed the the **getValue** and **setValue** methods.
77

8+
Note that virtual fields are created each time they are accessed and not stored or cached in the \PHPFUI\ORM\Record object. This means you can not change the virtual field on the Record object itself. You can only assign it to the Record object, which will store the value represented by the virtual field in the object, but not the virtual field itself.
9+
810
The **VirtualField** class has two properties that will always be defined for use by the derived class:
911
* $currentRecord is the current record that the virtual field should be based on.
1012
* $fieldName the field name that the VirtualField object was created from. This is the key of the $virtualFields array in the Record class.
@@ -125,3 +127,20 @@ foreach ($suppliers as $supplier)
125127
echo $supplier->company . "\n";
126128
}
127129
```
130+
131+
## Cast Virtual Field
132+
Often you want to use PHP class instead of a native scalar type (string, int, float, bool) to make your life easier. The Carbon class is an excellent example of a widely used package. It would be nice to get and set Carbon objects instead of strings formatted to the MySQL date format.
133+
134+
Use \PHPFUI\ORM\Cast virtual field to accommplish this. The Cast virtual field works with a wide variety of packages, as its only requirements are to implement **&lowbar;&lowbar;toString** and a construct from a value.
135+
### Usage
136+
```php
137+
class Invoice extends \Tests\App\Record\Definition\Order
138+
{
139+
protected static array $virtualFields = [
140+
'due_date' => [\PHPFUI\ORM\Cast::class, \Carbon\Carbon::class],
141+
'invoice_date' => [\PHPFUI\ORM\Cast::class, \Carbon\Carbon::class],
142+
];
143+
}
144+
$invoice = new Invoice(20);
145+
echo 'Lead Weeks: ' . $invoice->invoice_date->diffInWeeks($invoice->due_date);
146+
```

src/PHPFUI/ORM/Cast.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace PHPFUI\ORM;
4+
5+
class Cast extends \PHPFUI\ORM\VirtualField
6+
{
7+
public function getValue(array $parameters) : mixed
8+
{
9+
$class = \array_shift($parameters);
10+
11+
return new $class($this->currentRecord[$this->fieldName]);
12+
}
13+
14+
public function setValue(mixed $value, array $parameters) : void
15+
{
16+
$class = \array_shift($parameters);
17+
18+
if (! ($value instanceof $class))
19+
{
20+
throw new \PHPFUI\ORM\Exception(__METHOD__ . ': Error - ' . \get_debug_type($value) . ' is not an instance of ' . $class);
21+
}
22+
23+
$this->currentRecord[$this->fieldName] = "{$value}";
24+
}
25+
}

src/PHPFUI/ORM/Migration.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -484,12 +484,12 @@ private function alter(string $type, string $table, string $field, string $extra
484484
$this->alters[$table][] = $sql;
485485
}
486486

487-
private function getFieldInfo(string $table, string $field) : ?PHPFUI\ORM\Schema\Field
487+
private function getFieldInfo(string $table, string $fieldName) : ?\PHPFUI\ORM\Schema\Field
488488
{
489489
$fields = \PHPFUI\ORM::describeTable($table);
490490
foreach ($fields as $field)
491491
{
492-
if ($field->name == $field)
492+
if ($field->name == $fieldName)
493493
{
494494
return $field;
495495
}

0 commit comments

Comments
 (0)