diff --git a/LICENSE b/LICENSE index 344c7e5..6d4b293 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 b2pweb +Copyright (c) 2023 b2pweb Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0f4748b..ef41913 100755 --- a/README.md +++ b/README.md @@ -10,128 +10,42 @@ The goal of prime is to lightweight usage of data mapper and doctrine DBAL. [![Type Coverage](https://shepherd.dev/github/b2pweb/bdf-prime/coverage.svg)](https://shepherd.dev/github/b2pweb/bdf-prime) -### Installation with Composer +### Getting Started + +See [Wiki](https://github.com/b2pweb/bdf-prime/wiki) for more information ```bash composer require b2pweb/prime ``` -### Basic usage - -```PHP -declareConnection('myDB', [ - 'adapter' => 'mysql', - 'host' => 'localhost', -]); - -// Use the service locator to locate your repositories -$manager = new ServiceLocator($connexions); -$repository = $manager->repository(User::class); -/** @var User $user */ -$user = $repository->get(1); -$user->setName('john'); -$repository->save($user); -``` - -### Create your connections - ```PHP declareConnection('mysql', 'mysql://user:password@localhost/database'); -// Sqlite -$connexions->declareConnection('sqlite', 'sqlite://path/to/database.sqlite'); -``` - -You can also use [DBAL-compatible configuration arrays](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html) instead of DSN strings if you prefer: - -```PHP -declareConnection('mysql', [ - 'dbname' => 'mydb', - 'user' => 'user', - 'password' => 'secret', - 'host' => 'localhost', - 'driver' => 'pdo_mysql', - // OR - 'adapter' => 'mysql', -]); -``` - -#### Available options - -| Option | Type | Description | -|--------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------| -| `driver` | string | The driver name (ex: `pdo_mysql`, `mysqli`). See doctrine drivers for more informations. | -| `adapter` | string | PDO adapter. `pdo_` prefix will be added. | -| `dbname` | string | The database name. | -| `host` | string | The host name. | -| `port` | string | The connection port. | -| `user` | string | The user credentials. | -| `password` | string | The password credentials. | -| `path` | string | The file path used by sqlite to store data. | -| `url` | string | The DSN string. All data extract from the dsn will be erased the others options. | -| `driverOptions` | array | The driver options. | -| `memory` | bool | The sqlite option for memory. | -| `unix_socket` | string | The unix socket file. | -| `charset` | string | The client charset. | -| `server_version` | string | | -| `wrapper_class` | string | | -| `driver_class` | string | | -| `platform_service` | string | | -| `logging` | bool | | -| `shards` | array | Sharding connection: contains an array with shard name in key and options in value. The shard options will be merged onto the master connection. | -| `read` | array | Master/slave connection: contains an array of options for the read connection. | - - -### Create your mapper - -Use the class `Bdf\Prime\Mapper\Mapper` to create your mappers. - -#### Declare a mapper - -```PHP -import($data); + } +} +// Declare the data mapper for the entity class UserMapper extends Mapper { - /** - * Schema declaration - * - * Definition - * - connection : The connection name declare in connection manager (mandatory). - * - database : The database name. - * - table : The table name (mandatory). - * - tableOptions : The table options (ex: engine => myisam). - * - * - * return [ - * 'connection' => (string), - * 'database' => (string), - * 'table' => (string), - * 'tableOptions' => (array), - * ]; - * - * - * @return array|null - */ public function schema(): array { return [ @@ -140,348 +54,35 @@ class UserMapper extends Mapper ]; } - /** - * Build fields from this mapper. - * - * @param \Bdf\Prime\Mapper\Builder\FieldBuilder $builder - */ public function buildFields(FieldBuilder $builder): void { $builder ->bigint('id')->autoincrement() ->string('name') + ->string('password') + ->string('email') ; } - /** - * {@inheritdoc} - */ public function buildIndexes(IndexBuilder $builder): void { $builder->add()->on('name'); } } -``` - -#### The custom filters - -Prime allows custom filters definition. You can define filter alias for complex queries. - -```PHP - - * return [ - * 'customFilterName' => function( $query, $value) { - * return - * }, - * ]; - * - * - * @return array - */ - public function filters(): array - { - return [ - 'nameLike' => function($query, $value) { - $query->where('name', ':like', $value.'%'); - }, - ]; - } -... - -$users = $repository->where('nameLike', 'john')->all(); -``` - -#### The scopes - -The scope is a custom method of a repository. -```PHP - - * return [ - * 'customMethod' => function($repository, $test) { - * - * }, - * ]; - * - * $repository->customMethod('test'); - * - * @return array - */ - public function scopes(): array - { - return [ - 'countName' => function(\Bdf\Prime\Query\QueryInterface $query, $value) { - // you can access to the repository using $query->repository() - - $query->where('name', ':like', $value); - return $query->count(); - }, - ]; - } -... - -$count = $repository->countName('john'); -``` - -#### The sequence - -```PHP - myisam). - * - * - * return [ - * 'connection' => (string), - * 'table' => (string), - * 'column' => (string), - * 'tableOptions' => (array), - * ]; - * - * - * @return array - */ - public function sequence(): array - { - return [ - 'connection' => 'myDB', - 'table' => 'user_seq', - 'column' => 'id', - 'tableOptions' => [], - ]; - } -``` - -#### The model constraints - -You can define a model with default constraints. Those constraints will be applied for all queries on that model. - -```PHP -boolean('enabled') - ; - } - - /** - * Register custom event on notifier - * - * - * return [ - * 'attribute' => 'value' - * ] - * - * - * @return array - */ - public function customConstraints(): array - { - return [ - 'enabled' => true - ]; - } -} - -EnabledUser::all(); -// select * from EnabledUser where enabled = 1; -``` - -### Active record mode - -Prime enable an active record mode by using the class `Bdf\Prime\Entity\Model`. -This class provides shorcut method to the associative repository. -It also provides a default serialization with `Bdf\Serializer\Serializer`. - -```PHP -setName('john'); -$user->save(); -$user->delete(); -``` - -Static shortcuts for all repository methods (event scopes): -```PHP -all(); -$count = User::count(['name' => 'john'); -``` - -Access to the repository: -```PHP -on('customer') - ->belongsTo(Customer::class, 'customerId') - // only where customer.enabled = true - ->constraints(['enabled' => true]) - // Detach the relation: the attribute 'customer' will not be added on the entity. - ->detached() - ; - } -``` - -| *Methods* | *Description* | -|-------------------|-----------------------------------------------------------------------------------------------------------------| -| `belongsTo` | `$builder->on('customer')->belongsTo('Customer::id', 'customer.id');` | -| `hasOne` | `$builder->on('contact')->hasOne('Contact::distantId', 'localId');` | -| `hasMany` | `$builder->on('documents')->hasMany('Document::distantId', 'localId');` | -| `belongsToMany` | `$builder->on('packs')->belongsToMany('Pack::id', 'localId')->through('CustomerPack', 'customerId', 'packId');` | -| `morphTo` | | -| `morphOne` | | -| `morphMany` | | -| `inherit` | `$builder->on('target')->inherit('targetId');` Should be defined in subclasses | -| `custom` | | - - -#### Load relation - -```PHP -load('customer'); - -// You can load an object with its relations -$users = User::repository()->with('customer')->all(); - -``` - -#### Filter on relation - -```PHP -where('customer.name', 'like', 'Foo%')->all(); -// Note: those expressions are different: -$users = User::repository()->where('customer.id', 1)->all(); // Use join -$users = User::repository()->where('customerId', 1)->all(); // Don't join - -``` - -#### Custom relation - - -### Schema manager - -### Migration - -### Events - -Use the `Mapper::customEvents` method to declare your listeners -```PHP -saving(function($entity, $repository, $isNew) { - // do something - }); - } -``` - -The list of prime events: - -| *Event* | *Repository method* | *context* | -|---------------------|----------------------------|-----------| -| Events::POST_LOAD | `$repository->loaded()` | | -| Events::PRE_SAVE | `$repository->saving()` | | -| Events::POST_SAVE | `$repository->saved()` | | -| Events::PRE_INSERT | `$repository->inserting()` | | -| Events::POST_INSERT | `$repository->inserted()` | | -| Events::PRE_UPDATE | `$repository->updating()` | | -| Events::POST_UPDATE | `$repository->updated()` | | -| Events::PRE_DELETE | `$repository->deleting()` | | -| Events::POST_DELETE | `$repository->deleted()` | | +// Declare your connections +$connexions = new ConnectionManager(); +$connexions->declareConnection('myDB', 'mysql://myuser:mypassword@localhost'); -### Behaviors +// Use the service locator to locate your repositories +$manager = new ServiceLocator($connexions); +Locatorizable::configure($manager); +$repository = $manager->repository(User::class); -You can defined the model behaviors in your mapper using the method `Mapper::getDefinedBehaviors`. +// Get and update an entity +$user = User::findById(1); +$user->setName('john')->save(); -```PHP -orWhere('email', (new Like('john%'))->startsWith())->all(); ``` - -Available behaviors: - -| *Class name* | *Description* | -|---------------------------------------|--------------------------------------------------------------------------------| -| `Bdf\Prime\Behaviors\Blameable` | Automatically add user name that has realize update or creation on the entity. | -| `Bdf\Prime\Behaviors\SoftDeletable` | Mark entity as deleted but keep the data in storage. | -| `Bdf\Prime\Behaviors\Timestampable` | Automatically add created and updated date time on the entity. | -| `Bdf\Prime\Behaviors\Versionable` | |