ornament / core
PHP8 ORM toolkit, core package
Requires
- php: >=8.1
Requires (Dev)
- gentry/gentry: ^0.16.0
- toast/unit: ^2
Suggests
- monolyth/formulaic: PHP8 OO-forms with data binding for (Ornament) models
- dev-master
- 0.17.0
- 0.16.5
- 0.16.4
- 0.16.3
- 0.16.2
- 0.16.1
- 0.16.0
- 0.15.9
- 0.15.8
- 0.15.7
- 0.15.6
- 0.15.5
- 0.15.4
- 0.15.3
- 0.15.2
- 0.15.1
- 0.15.0
- 0.14.0
- 0.13.1
- 0.13.0
- 0.12.2
- 0.12.1
- 0.12.0
- 0.11.10
- 0.11.9
- 0.11.8
- 0.11.7
- 0.11.6
- 0.11.5
- 0.11.4
- 0.11.3
- 0.11.2
- 0.11.1
- 0.11.0
- 0.10.6
- 0.10.5
- 0.10.4
- 0.10.3
- 0.10.2
- 0.10.1
- 0.10.0
- 0.9.3
- 0.9.2
- 0.9.1
- 0.9.0
- 0.8.3
- 0.8.2
- 0.8.1
- 0.8.0
- 0.7.1
- 0.7.0
- 0.6.16
- 0.6.15
- 0.6.14
- 0.6.13
- 0.6.12
- 0.6.11
- 0.6.10
- 0.6.9
- 0.6.8
- 0.6.7
- 0.6.6
- 0.6.5
- 0.6.4
- 0.6.3
- 0.6.2
- 0.6.1
- 0.6.0
- 0.5.2
- 0.5.1
- 0.5.0
- 0.4.2
- 0.4.1
- 0.4.0
- 0.3.0
- 0.2.0
- 0.1.2
- 0.1.1
- 0.1.0
- 0.0.2
- 0.0.1
- dev-dependabot/composer/twig/twig-3.14.2
- dev-develop
This package is auto-updated.
Last update: 2025-03-02 12:46:31 UTC
README
PHP8 object decorator toolkit, core package
When translating data from any storage source to your PHP models, you'll often
want to augment it. For example, a date from a database is simply a string,
but maybe you prefer it as a DateTime
object, or a Carbon
instance.
Ornament's aim is to simplify tons of boilerplate code to facilitate this.
Installation
$ composer require ornament/core
You'll likely also want auxiliary packages from the ornament/*
family. These
contain predefined types you can use, e.g. the Bitflag\Property
allowing you
to stuff multiple true/false
values in a single integer.
Manual installation
Yadiyadi, git clone
or whatever and add the path for the Ornament\Core
namespace to your psr-4
autoload config. But really, don't do that unless
you're masochistic.
Basic usage
Ornament models are nothing more than vanilla PHP classes; there is no need to extend any master or god class of sorts (you might want to do that in your own framework of choice).
At the very least, you'll want to use
the Ornament\Core\Model
trait:
<?php use Ornament\Core\Model; class FooModel { use Model; public readonly DateTime $datecreated; }
Here, we indicated (via type-hinting) that the datecreated
property should
actually contain a DateTime
object. It is also readonly, since the creation
date is not very likely to ever change.
Ornament defines a default constructor which assumes one argument: a hash of key/value pairs for your model's properties. If you require a constructor for your model (e.g. because you need to inject dependencies) you may alias the default constructor and call it manually. An example:
<?php use Ornament\Core\Model; class FooModel { use Model { Model::__construct as ornamentConstruct; } public function __construct(private MyDependency $foo, ?iterable $input = null) { $this->ornamentConstruct($input); } }
Decorated properties assume the decorating class accepts the value as its
first constructor argument. Additional arguments may be specified by annotating
the property with one or more Ornament\Core\Construct
attributes. The
attribute's constructor argument is the actual additional value that should be
passed. For instance, to specify a specific time zone for the DateTime
property, one would write:
<?php use Ornament\Core\{ Model, Construct }; class FooModel { use Model; #[Construct(new DateTimeZone('Europe/Amsterdam'))] public readonly DateTime $datecreated; }
Lastly, Ornament defines a Decorator
class that custom decorators may extend.
The difference from a "vanilla" decorator is that extensions of the Decorator
come with some utility methods, and receive by default a second constructor
argument: a ReflectionProperty
of the original target field (so your custom
decorator can also refer to any attributes your project may have defined for
that specific property - or whatever other black magic you need to do).
Extensions of the Decorator
class can also add Construct
attributes like
above. These will be passed as the third etc. arguments to the constructor.
Types
Properties specifying one of the basic, built-in types (e.g. int
or string
)
will have their values coerced to the correct type.
Enums
Properties may also be type-hinted as a backed enum:
<?php enum MyEnum { case foo = 1; case bar = 2; } class MyModel { public MyEnum $baz; } // Fails: 3 is not in the enum! $model = new MyModel(['baz' => 3]);
If the enum property is nullable, an invalid value will not throw an error,
but cause null
to be set instead.
Getters for virtual properties
Ornament models support the concept of virtual properties (which are, by definition, read-only).
An example of a virtual property would be a model with a firstname
and
lastname
property, and a getter for fullname
. To mark a method as a getter,
attribute it with #[\Ornament\Core\Getter("propertyName")]
:
<?php class MyModel { // ... #[\Ornament\Core\Getter("fullname")] protected function exampleGetter() : string { return $this->firstname.' '.$this->lastname; } }
The name and visibility of a getter (usually) don't matter; a best practice is
to mark them as protected
so they cannot be called from outside, and to give
them a reasonably descriptive name for your own sanity (in the above example,
getFullname
would have been better).
PHP, PDO and fetchObject
PDO's fetchObject
and related methods try to be clever by injecting
properties based on fetched database columns before the constructor is called.
Hence, do not use PDO::FETCH_OBJECT
; instead, use PDO::FETCH_ASSOC
.
Stateful models
Models may use the Ornament\Core\State
trait to expose some convenience
methods:
isDirty()
: was the model changed since instantiation?isModified(string $property)
: specifically check if a property was modified.isPristine()
: the opposite ofisDirty
.markPristine()
: manually mark the model as pristine, e.g. after storing it. Basically this resets the initial state to the current state.
All these methods are public. You can use them in your storage logic to
determine how to proceed (e.g. skip an expensive UPDATE
operation if the model
isPristine()
anyway).
Preventing properties from being decorated
If your models are more than a "simple" data store, there might be properties on it you explicitly don't want decorated. Note that any static property is already left alone.
To explicitly tell Ornament to skip decoration for a public or protected
property, add the attribute Ornament\Core\NoDecoration
to it.