harp-orm / harp
Light and Flexible ORM without magic
Requires
- php: >=5.4.0
- harp-orm/query: ~0.2.0
- harp-orm/serializer: ~0.1.0
- harp-orm/util: ~0.2.2
- harp-orm/validate: ~0.4.0
This package is auto-updated.
Last update: 2024-12-21 21:31:07 UTC
README
Harp ORM
Harp ORM is a light DataMapper persistence layer for php objects.
Quick example
// Model Class use Harp\Harp\AbstractModel; class UserModel extends AbstractModel { public static function initialize($config) { $config ->addRel(new Rel\BelongsTo('address', $config, Address::getRepo())); } public $id; public $name; public $email; public $addressId; public function getAddress() { return $this->get('address'); } public function setAddress(Address $address) { return $this->set('address', $address); } } // Saving new model $user = new UserModel(['name' => 'my name']); UserRepo::save($user); // Loading model $loadedUser = UserRepo::find(1); var_dump($loadedUser); // Loading related model $address = $loadedUser->getAddress(); var_dump($loadedUser->getAddress());
Why?
Why another ORM? At present there are no ORMs that use the latest PHP features. Recent advancements in the php language itself (e.g. traits), and external static analysis tools allow for writing applications that can be easily verified to be correct, however most available ORMs don't use them which makes static code analysis not very useful. And lastly, this package aims to be closer to the metal, smartly using existing PHP classes and extensions to make your models fast, without sacraficing features. Here's the elevator pitch:
- Uses harp-orm/query and PDO as much as possible greatly increasing performance. It has some very useful features that are not used by current ORMs
- Full polymorphism support - both "belongs to polymorphic" and "single table inheritance"
- Proper soft delete, which every part of the code is aware of
- Lazy loading and eager loading, which works for polymorphic relations
- Save multiple models with grouped queries for increased performance
- No enforcement of folder structure, place your classes wherever you want
- Uses PSR coding style and symfony naming conventions for more clean and readable codebase
- Save all associated models with a single command, with query grouping under the hood.
- Fully extensible interface. Uses native PHP5 constructs to allow extending with traits and interfaces
- All methods have proper docblocks so that static code analyses of code built on top of this is more accurate.
Instalation
Harp uses composer so intalling it is as easy as:
composer require harp-orm/harp:~0.3.0
It uses harp-orm/query for connecting to the database, so you'll also need to configure the connection:
use Harp\Query\DB; DB::setConfig([ 'dsn' => 'mysql:dbname=harp-orm/harp;host=127.0.0.1', 'username' => 'root', 'password' => 'root', ]);
Subsections
Defining Models
Here's an example model class.
use Harp\Harp\AbstractModel; class User extends AbstractModel { // Configure the "Repo" object for this model class // This holds all the database-specific configs, // as well as relations with other models public static function initialize($config) { $config // Configure relations ->addRel(new Rel\BelongsTo('address', $config, AddressRepo::get())); // Configure validations ->addAssert(new Assert\Present('name')) ->addAssert(new Assert\Email('name')); } // Public properties persisted as columns in the table public $id; public $name; public $email; public $addressId; }
All the public properties get persisted in the database, using the native types if available.
Tip Once related objects have been loaded, they will be cached and returned on further requests, however the data is not kept in the model itself, thus if you do a
var_dump
on an model it will return only data of the model itself and will keep your stack traces readable.
Detailed list of all the configuration methods:
For more about events, read Extending using events
Retrieving from the database
Retrieving models from the database (as well as saving but on that later) are handled with static methods on the model class. To find models by their primary key use the find
method.
$user1 = User::find(8); $user2 = User::find(23);
If the model has a "name" property (or a nameKey configured) you can use findByName
method.
$user1 = User::findByName('Tom'); $user2 = User::findByName('John');
For more complex retrieving you can use the findAll
method, which returns a 'Find' object. It has a rich interface of methods for constructing an sql query:
$select = User::findAll(); $select ->where('name', 'John') ->whereIn('type', [1, 4]) ->joinRels(['address' => 'city']) ->limit(10); $users = $select->load(); foreach ($users as $user) { var_dump($user); }
All the models retrieved from the database are stored in an "identity map". So that if at a later time, the same model is loaded again. It will return the same php object, associated with the db row.
$user1 = User::find(10); $user2 = User::find(10); // Will return true echo $user1 === $user2;
Detailed docs for findAll
Persisting Models
When models have been created, modified or deleted they usually need to be persisted again. This is done with the "save" method on the model.
$user = User::find(10); $user->name = 'new name'; $address = new Address(['location' => 'home']); $user->setAddress($address); // This will save the user, the address and the link between them. User::save($user); $user2 = User::find(20); $user2->delete(); // This will remove the deleted user from the database. User::save($user2);
When you add / remove or otherwise modify related models they will be saved alongside your main model.
Preserving multiple models
You can presever multiple models of the same repo at once (with query grouping) using the saveArray
method. Just pass an array of models.
$save = User::saveArray([$user1, $user2, $user3]);
Soft deletion
If you need to keep the models in the database even after they are deleted - e.g. logical deltion, you can use the SoftDeleteTrait
// Model File use Harp\Harp\AbstractModel; use Harp\Harp\Model\SoftDeleteTrait; class Order extends AbstractModel { use SoftDeleteTrait; public static function initialize($config) { // The trait has its own initialize method that you'll have to call SoftDeleteTrait::initialize($config); } } $order = Order::find(2); $order->delete(); // This will issue an UPDATE instaead of a DELETE, marking this row as "deleted". Order::save($order); $order = Order::find(2); // This will return true echo $order->isVoid();
This adds a some of methods to your model. Read about soft deletion in detail here
Inherited
Sometimes you need several models to share the same database table - e.g. if there is just a slight variation of the same functionality. This is called Single Table Inheritance.
Harp ORM supports inheriting models out of the box. Read about inexperience in detail here
Model states
Throughout their lives Models have different states (Harp\Harp\Model\State). They can be:
There are several methods for working with model states:
Dirty Tracking
Models track all the changes to their public properties to minimise updates to the database. You can use that functionality yourself by calling these methods:
Example:
$user = User::find(10); // returns false echo $user->isChanged(); $user->name = 'new test'; // returns true $user->isChanged(); // returns true $user->hasChange('name'); // returns ['name' => 'new test'] $user->getChanges(); // returns original name $user->getOriginal('name'); $user->resetOriginal(); // returns 'new test' $user->getOriginal('name');
Extending
When you want to write packages that extend functionality of Harp ORM, or simply share code between your models, you can use PHP's native Traits. They allow you to statically extends classes. All of the internals of Harp ORM are built around allowing you to accomplish this easily as this is the preferred way of writing "behaviours" or "templates".
Apart from that you will be able to add event listeners for various events in the life-cycle of models.
Read about extending in detail here
Direct database access.
There are times when you'll need to get to the bare metal and write custom sqls. To help you do that you can use the internal Query classes directly.
$update = User::insertAll() ->columns(['name', 'id']) ->select( Profile::selectAll() ->clearColumns() ->column('name') ->column('id') ->where('name', 'LIKE', '%test') ); // INSERT INTO User (name, id) SELECT name, id FROM Profile WHERE name LIKE '%test' $update->execute();
More details about custom queries you can read the Query section
License
Copyright (c) 2014, Clippings Ltd. Developed by Ivan Kerin as part of clippings.com
Under BSD-3-Clause license, read LICENSE file.
Icon made by Freepik from www.flaticon.com