diff --git a/config/doctrine.php b/config/doctrine.php index 9c37db7a..e99d9e39 100644 --- a/config/doctrine.php +++ b/config/doctrine.php @@ -12,7 +12,7 @@ | paths setting to the appropriate path and replace App namespace | by your own namespace. | - | Available meta drivers: attributes|fluent|xml|simplified_xml|static_php|php + | Available meta drivers: attributes|xml|simplified_xml|static_php|php | | Available connections: mysql|oracle|pgsql|sqlite|sqlsrv | (Connections can be configured in the database config) diff --git a/docs/auth.md b/docs/auth.md deleted file mode 100644 index e8e6e9ba..00000000 --- a/docs/auth.md +++ /dev/null @@ -1,164 +0,0 @@ -# Authentication - -## Configuration - -### Implementing Authenticatable - -First you must extend Laravel's authentication contract on the entity you wish to use with authentication. - -``` -class User implements \Illuminate\Contracts\Auth\Authenticatable -{ - - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - */ - protected $id; - - public function getAuthIdentifierName() - { - return 'id'; - } - - public function getAuthIdentifier() - { - return $this->id; - } - - public function getPassword() - { - return $this->password; - } -} -``` - -You may also use the provided trait `LaravelDoctrine\ORM\Auth\Authenticatable` in your entity and override where necessary. - - -``` -class User implements \Illuminate\Contracts\Auth\Authenticatable -{ - use \LaravelDoctrine\ORM\Auth\Authenticatable; - - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - */ - protected $userId; - - public function getAuthIdentifierName() - { - return 'userId'; - } -} -``` - -### Configuring Laravel - -Edit Laravel's Auth configuration (`/config/auth.php`) to set up use with Doctrine. - -``` -return [ - - /* - |-------------------------------------------------------------------------- - | Default Authentication Driver - |-------------------------------------------------------------------------- - | - | This option controls the authentication driver that will be utilized. - | This driver manages the retrieval and authentication of the users - | attempting to get access to protected areas of your application. - | - | - */ - - 'driver' => 'doctrine', - - /* - |-------------------------------------------------------------------------- - | Authentication Model - |-------------------------------------------------------------------------- - | - | This is the entity that has implemented Authenticatable - | - */ - - 'model' => App\Entities\User::class, - - - /* - |-------------------------------------------------------------------------- - | Password Reset Settings - |-------------------------------------------------------------------------- - | - | Here you may set the options for resetting passwords including the view - | that is your password reset e-mail. You can also set the name of the - | table that maintains all of the reset tokens for your application. - | - | The expire time is the number of minutes that the reset token should be - | considered valid. This security feature keeps tokens short-lived so - | they have less time to be guessed. You may change this as needed. - | - */ - - 'password' => [ - 'email' => 'emails.password', - 'table' => 'password_resets', - 'expire' => 60, - ], - -]; -``` - -## Password hashing -Password hashing must be handled by your application; Laravel's authentication -and LaravelDoctrine will treat passwords as nothing more than strings. We would -recommend decoupling the operation of hashing of the password (and any other -procedures, like validating strength) from its storage by implementing a separate -service to handle any password-related actions. - -``` -use \Illuminate\Contracts\Hashing\Hasher; - -class PasswordService -{ - private $hasher; - private $passwordStrengthValidator; - - /** - * @param Hasher $hasher - * @param MyPasswordStrengthValidator $passwordStrength - */ - public function __construct( - Hasher $hasher, - MyPasswordStrengthValidator $passwordStrength - ) { - $this->hasher = $hasher; - $this->passwordStrengthValidator = $passwordStrength - } - - /** - * Validate and change the given users password - * - * @param User $user - * @param string $password - * @throws PasswordTooWeakException - * @return void - */ - public function changePassword(User $user, $password) - { - if ($this->passwordStrengthValidator->isStrongEnough($password)) { - $user->setPassword($this->hasher->make($password)) - } else { - throw new PasswordTooWeakException(); - } - } -} -``` - -## Using Authentication - -Authentication usage is covered by [Laravel's Documentation.](https://laravel.com/docs/authentication) diff --git a/docs/auth.rst b/docs/auth.rst new file mode 100644 index 00000000..4cf0a5b8 --- /dev/null +++ b/docs/auth.rst @@ -0,0 +1,183 @@ +============== +Authentication +============== + +Configuration +============= + +Implementing Authenticatable +---------------------------- + +First you must extend Laravel's authentication contract on the entity you +wish to use with authentication. + +.. code-block:: php + + class User implements \Illuminate\Contracts\Auth\Authenticatable + { + + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + */ + protected $id; + + public function getAuthIdentifierName() + { + return 'id'; + } + + public function getAuthIdentifier() + { + return $this->id; + } + + public function getPassword() + { + return $this->password; + } + } + + +You may also use the provided trait ``LaravelDoctrine\ORM\Auth\Authenticatable`` +in your entity and override where necessary. + + +.. code-block:: php + + class User implements \Illuminate\Contracts\Auth\Authenticatable + { + use \LaravelDoctrine\ORM\Auth\Authenticatable; + + #[ORM\Id] + #[ORM\Column(type: "integer")] + #[ORM\GeneratedValue(strategy: "AUTO")] + protected $userId; + + public function getAuthIdentifierName() + { + return 'userId'; + } + } + + +Configuring Laravel +------------------- + +Edit Laravel's Auth configuration ``/config/auth.php`` to set up use with Doctrine. + + +.. code-block:: php + + return [ + + /* + |-------------------------------------------------------------------------- + | Default Authentication Driver + |-------------------------------------------------------------------------- + | + | This option controls the authentication driver that will be utilized. + | This driver manages the retrieval and authentication of the users + | attempting to get access to protected areas of your application. + | + | + */ + + 'driver' => 'doctrine', + + /* + |-------------------------------------------------------------------------- + | Authentication Model + |-------------------------------------------------------------------------- + | + | This is the entity that has implemented Authenticatable + | + */ + + 'model' => App\Entities\User::class, + + + /* + |-------------------------------------------------------------------------- + | Password Reset Settings + |-------------------------------------------------------------------------- + | + | Here you may set the options for resetting passwords including the view + | that is your password reset e-mail. You can also set the name of the + | table that maintains all of the reset tokens for your application. + | + | The expire time is the number of minutes that the reset token should be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + */ + + 'password' => [ + 'email' => 'emails.password', + 'table' => 'password_resets', + 'expire' => 60, + ], + + ]; + + +Password hashing +================ + +Password hashing must be handled by your application. Laravel's authentication +and LaravelDoctrine will treat passwords as nothing more than strings. We would +recommend decoupling the operation of hashing of the password (and any other +procedures, like validating strength) from its storage by implementing a separate +service to handle any password-related actions. + +.. code-block:: php + use \Illuminate\Contracts\Hashing\Hasher; + + class PasswordService + { + private $hasher; + private $passwordStrengthValidator; + + /** + * @param Hasher $hasher + * @param MyPasswordStrengthValidator $passwordStrength + */ + public function __construct( + Hasher $hasher, + MyPasswordStrengthValidator $passwordStrength + ) { + $this->hasher = $hasher; + $this->passwordStrengthValidator = $passwordStrength + } + + /** + * Validate and change the given users password + * + * @param User $user + * @param string $password + * @throws PasswordTooWeakException + * @return void + */ + public function changePassword(User $user, $password) + { + if ($this->passwordStrengthValidator->isStrongEnough($password)) { + $user->setPassword($this->hasher->make($password)) + } else { + throw new PasswordTooWeakException(); + } + } + } + + +Using Authentication +==================== + +Authentication usage is covered by +`Laravel's Documentation `_. + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/banner.png b/docs/banner.png new file mode 100644 index 00000000..5a3e0803 Binary files /dev/null and b/docs/banner.png differ diff --git a/docs/caching.md b/docs/caching.md deleted file mode 100644 index 26094a97..00000000 --- a/docs/caching.md +++ /dev/null @@ -1,25 +0,0 @@ -# Caching - -This package supports these caching systems out of the box: - -* redis -* memcached -* file -* apc -* array - -## Extending or Adding Cache Drivers - -Drivers can be replaced or added using `LaravelDoctrine\ORM\Configuration\Cache\CacheManager`. The return should implement `Doctrine\Common\Cache\Cache` or extend `Doctrine\Common\Cache\CacheProvider` - -```php -public function register() -{ - $this->app->resolving(CacheManager::class, function (CacheManager $cache){ - $cache->extend('memcache', function(array $settings, Application $app) { - $memcache = new \Memcache; - return new MemcacheCache($memcache); - }); - }); -} -``` diff --git a/docs/caching.rst b/docs/caching.rst new file mode 100644 index 00000000..f77c149a --- /dev/null +++ b/docs/caching.rst @@ -0,0 +1,36 @@ +======= +Caching +======= + +This package supports these caching systems out of the box: + +* redis +* memcached +* file +* apc +* array + +Extending or Adding Cache Drivers +================================= + +Drivers can be replaced or added using +``LaravelDoctrine\ORM\Configuration\Cache\CacheManager``. The return should +implement ``Doctrine\Common\Cache\Cache`` or extend ``Doctrine\Common\Cache\CacheProvider`` + +.. code-block:: php + + public function register() + { + $this->app->resolving(CacheManager::class, function (CacheManager $cache){ + $cache->extend('memcache', function(array $settings, Application $app) { + $memcache = new \Memcache; + return new MemcacheCache($memcache); + }); + }); + } + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/config-file.md b/docs/config-file.md deleted file mode 100644 index 1663ae00..00000000 --- a/docs/config-file.md +++ /dev/null @@ -1,402 +0,0 @@ -# Configuration File Overview - -- [Sample Configuration](#sample-configuration) -- [Entity Manager](#entity-manager) - - [Namespace Alias](#entity-manager-namespace-alias) -- [Extensions](#extensions) -- [Custom Types](#custom-types) -- [Custom Functions](#custom-functions) -- [Custom Hydration Modes](#custom-hydration-modes) -- [Logger](#logger) -- [Cache](#cache) -- [Gedmo Extensions](#gedmo) - -This page provides a quick overview of all of the options provided in `doctrine.php`, the configuration file for Laravel Doctrine. - -### Sample Configuration - -```php - Warning: Proxy auto generation should only be enabled in dev! - | - */ - 'managers' => [ - 'default' => [ - 'dev' => env('APP_DEBUG'), - 'meta' => env('DOCTRINE_METADATA', 'annotations'), - 'connection' => env('DB_CONNECTION', 'mysql'), - 'namespaces' => [ - 'App' - ], - 'paths' => [ - base_path('app') - ], - 'repository' => Doctrine\ORM\EntityRepository::class, - 'proxies' => [ - 'namespace' => false, - 'path' => storage_path('proxies'), - 'auto_generate' => env('DOCTRINE_PROXY_AUTOGENERATE', false) - ], - /* - |-------------------------------------------------------------------------- - | Doctrine events - |-------------------------------------------------------------------------- - | - | The listener array expects the key to be a Doctrine event - | e.g. Doctrine\ORM\Events::onFlush - | - */ - 'events' => [ - 'listeners' => [], - 'subscribers' => [] - ], - 'filters' => [] - ] - ], - /* - |-------------------------------------------------------------------------- - | Doctrine Extensions - |-------------------------------------------------------------------------- - | - | Enable/disable Doctrine Extensions by adding or removing them from the list - | - | If you want to require custom extensions you will have to require - | laravel-doctrine/extensions in your composer.json - | - */ - 'extensions' => [ - //LaravelDoctrine\ORM\Extensions\TablePrefix\TablePrefixExtension::class, - //LaravelDoctrine\Extensions\Timestamps\TimestampableExtension::class, - //LaravelDoctrine\Extensions\SoftDeletes\SoftDeleteableExtension::class, - //LaravelDoctrine\Extensions\Sluggable\SluggableExtension::class, - //LaravelDoctrine\Extensions\Sortable\SortableExtension::class, - //LaravelDoctrine\Extensions\Tree\TreeExtension::class, - //LaravelDoctrine\Extensions\Loggable\LoggableExtension::class, - //LaravelDoctrine\Extensions\Blameable\BlameableExtension::class, - //LaravelDoctrine\Extensions\IpTraceable\IpTraceableExtension::class, - //LaravelDoctrine\Extensions\Translatable\TranslatableExtension::class - ], - /* - |-------------------------------------------------------------------------- - | Doctrine custom types - |-------------------------------------------------------------------------- - */ - 'custom_types' => [ - 'json' => LaravelDoctrine\ORM\Types\Json::class - ], - /* - |-------------------------------------------------------------------------- - | DQL custom datetime functions - |-------------------------------------------------------------------------- - */ - 'custom_datetime_functions' => [], - /* - |-------------------------------------------------------------------------- - | DQL custom numeric functions - |-------------------------------------------------------------------------- - */ - 'custom_numeric_functions' => [], - /* - |-------------------------------------------------------------------------- - | DQL custom string functions - |-------------------------------------------------------------------------- - */ - 'custom_string_functions' => [], - /* - |-------------------------------------------------------------------------- - | Register custom hydrators - |-------------------------------------------------------------------------- - */ - 'custom_hydration_modes' => [ - // e.g. 'hydrationModeName' => MyHydrator::class, - ], - /* - |-------------------------------------------------------------------------- - | Enable query logging with laravel file logging, - | debugbar, clockwork or an own implementation. - | Setting it to false, will disable logging - | - | Available: - | - LaravelDoctrine\ORM\Loggers\LaravelDebugbarLogger - | - LaravelDoctrine\ORM\Loggers\ClockworkLogger - | - LaravelDoctrine\ORM\Loggers\FileLogger - |-------------------------------------------------------------------------- - */ - 'logger' => env('DOCTRINE_LOGGER', false), - /* - |-------------------------------------------------------------------------- - | Cache - |-------------------------------------------------------------------------- - | - | Configure meta-data, query and result caching here. - | Optionally you can enable second level caching. - | - | Available: acp|array|file|memcached|redis - | - */ - 'cache' => [ - 'second_level' => false, - 'default' => env('DOCTRINE_CACHE', 'array'), - 'namespace' => null, - 'metadata' => [ - 'driver' => env('DOCTRINE_METADATA_CACHE', env('DOCTRINE_CACHE', 'array')), - 'namespace' => null, - ], - 'query' => [ - 'driver' => env('DOCTRINE_QUERY_CACHE', env('DOCTRINE_CACHE', 'array')), - 'namespace' => null, - ], - 'result' => [ - 'driver' => env('DOCTRINE_RESULT_CACHE', env('DOCTRINE_CACHE', 'array')), - 'namespace' => null, - ], - ], - /* - |-------------------------------------------------------------------------- - | Gedmo extensions - |-------------------------------------------------------------------------- - | - | Settings for Gedmo extensions - | If you want to use this you will have to require - | laravel-doctrine/extensions in your composer.json - | - */ - 'gedmo' => [ - 'all_mappings' => false - ] -]; -``` - -### Entity Manager - -An **Entity Manager (EM)** contains all of the information Doctrine needs to understand, retrieve, and manipulate a set of entities (models). - -You must have **at least one** EM in order to use Laravel Doctrine. - -To use more than one EM simply create another entry in the `managers` array. - -| Property | Explanation | -|:-----------|------------| -| **EM Name** | In the sample below the EM we have configured is named `default`. This is the EM that Laravel Doctrine will attempt to use if no argument is provided to `ManagerRegistry`. | -| **dev** | Whether this EM is in development mode. | -| **meta** | The metadata driver to use. Built-in options are `fluent|annotations|yaml|simplified_yaml|xml|simplified_xml|config|static_php|php` | -| **connection** | The connection driver to use. Built-in options are `mysql|oracle|pgsql|sqlite|sqlsrv` | -| **namespaces** | (Optional) If your entities are not located in the configured app namespace you can specify a different one here. | -| **paths** | An paths where the mapping configurations for your entities is located. | -| **repository** | (Optional) The default repository to use for this EM. | -| **decorator** | (Optional) Your custom EM decorator to overwrite the default EM. | -| **proxies.namespace** | Namespace (if different) specified for proxy classes | -| **proxies.path** | The path where proxy classes should be generated. | -| **proxies.auto_generate** | Should proxy classes be generated every time an EM is created? (Turn off production) | -| **events.subscribers** |Subscribers should implement `Doctrine\Common\EventSubscriber` | -| **events.listeners** | Key should be event type. E.g. `Doctrine\ORM\Events::onFlush`. Value should be the listener class | -| **filters** | Filter system that allows the developer to add SQL to the conditional clauses of queries, regardless the place where the SQL is generated | - - -```php - 'managers' => [ - 'default' => [ - 'dev' => env('APP_DEBUG'), - 'meta' => env('DOCTRINE_METADATA', 'annotations'), - 'connection' => env('DB_CONNECTION', 'mysql'), - 'namespaces' => [ - 'App' - ], - 'paths' => [ - base_path('app') - ], - 'repository' => Doctrine\ORM\EntityRepository::class, - 'proxies' => [ - 'namespace' => false, - 'path' => storage_path('proxies'), - 'auto_generate' => env('DOCTRINE_PROXY_AUTOGENERATE', false) - ], - 'events' => ... - 'filters' => ... - ] - ] -``` - -#### Namespace Alias - -To use namespace alias, you just have to specify then as key of each namespace. - -Example: -```php - 'managers' => [ - 'default' => [ - ... - 'connection' => env('DB_CONNECTION', 'mysql'), - 'namespaces' => [ - 'Foo' => 'App\Model\Foo\Entities', - 'Bar' => 'App\Model\Bar\Entities', - ], - 'paths' => [ - base_path('app') - ], - ... - ] - ] -``` - -Whenever you need to specify entities in these namespaces, you can simple use the alias as follow: -```php - SELECT f FROM Foo:SomeEntity - or - \EntityManager::getRepository('Bar:SomeEntity'); -``` - -### Extensions - -Extensions can be enabled by adding them to this array. They provide additional functionality Entities (Timestamps, Loggable, etc.) - -To use the extensions in this sample you must install the extensions package: - -``` -require laravel-doctrine/extensions -``` - -and follow the [installation instructions.](http://www.laraveldoctrine.org/docs/current/extensions/installation) - -```php -'extensions' => [ - //LaravelDoctrine\ORM\Extensions\TablePrefix\TablePrefixExtension::class, - //LaravelDoctrine\Extensions\Timestamps\TimestampableExtension::class, - //LaravelDoctrine\Extensions\SoftDeletes\SoftDeleteableExtension::class, - //LaravelDoctrine\Extensions\Sluggable\SluggableExtension::class, - //LaravelDoctrine\Extensions\Sortable\SortableExtension::class, - //LaravelDoctrine\Extensions\Tree\TreeExtension::class, - //LaravelDoctrine\Extensions\Loggable\LoggableExtension::class, - //LaravelDoctrine\Extensions\Blameable\BlameableExtension::class, - //LaravelDoctrine\Extensions\IpTraceable\IpTraceableExtension::class, - //LaravelDoctrine\Extensions\Translatable\TranslatableExtension::class -], -``` - -### Custom Types - -Custom types are classes that allow Doctrine to marshal data to/from the data source in a custom format. - -To register a custom type simple add the class to this list. [For more information on custom types refer to the Doctrine documentation.](https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/cookbook/custom-mapping-types.html) - -### Custom Functions - -These are classes that extend the functionality of Doctrine's DQL language. More information on what functions are available [visit the repository.](https://github.com/beberlei/DoctrineExtensions) - -To use the extensions in this sample you must install the extensions package: - -``` -require laravel-doctrine/extensions -``` - -and follow the [installation instructions.](http://www.laraveldoctrine.org/docs/current/extensions/installation) - -If you include `BeberleiExtensionsServiceProvider` all custom functions will automatically be registered. - -To add a function simply add it to the correct list using this format: - -`'FUNCTION_NAME' => 'Path\To\Class'` - -```php -/* -|-------------------------------------------------------------------------- -| DQL custom datetime functions -|-------------------------------------------------------------------------- -*/ -'custom_datetime_functions' => [], -/* -|-------------------------------------------------------------------------- -| DQL custom numeric functions -|-------------------------------------------------------------------------- -*/ -'custom_numeric_functions' => [], -/* -|-------------------------------------------------------------------------- -| DQL custom string functions -|-------------------------------------------------------------------------- -*/ -'custom_string_functions' => [], -``` - -### Custom Hydration Modes - -This option enables you to register your Hydrator classes to use as custom hydration modes. For more information about custom hydration modes see [doctrine documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#custom-hydration-modes). - -To register custom hydrator, add it to the list in following format: - -`'hydrationModeName' => MyHydrator::class` - -```php -/* -|-------------------------------------------------------------------------- -| Register custom hydrators -|-------------------------------------------------------------------------- -*/ -'custom_hydration_modes' => [ - // e.g. 'hydrationModeName' => MyHydrator::class, -], -``` - -### Logger - -Enable logging of Laravel Doctrine and Doctrine by using the logger functionality. - -|Available loggers| -|--| -| `LaravelDoctrine\ORM\Loggers\LaravelDebugbarLogger` | -| `LaravelDoctrine\ORM\Loggers\ClockworkLogger` | -| `LaravelDoctrine\ORM\Loggers\FileLogger` | - -` 'logger' => env('DOCTRINE_LOGGER', false),` - -### Cache - -Cache will be used to cache metadata, results and queries. - -**Available cache providers:** - -* apc -* array -* file -* memcached -* redis - -** Config settings:** - -|Property|Explanation | -|--|--| -| **cache.default** | The default cache provider to use. | -| **cache.namespace** | Will add namespace to the cache key. This is useful if you need extra control over handling key names collisions in your Cache solution.| -| **cache.second_level** | The Second Level Cache is designed to reduce the amount of necessary database access. It sits between your application and the database to avoid the number of database hits as much as possible. When turned on, entities will be first searched in cache and if they are not found, a database query will be fired an then the entity result will be stored in a cache provider. When used, READ_ONLY is mostly used. ReadOnly cache can do reads, inserts and deletes, cannot perform updates| -| **cache.metadata** | Your class metadata can be parsed from a few different sources like YAML, XML, Annotations, etc. Instead of parsing this information on each request we should cache it using one of the cache drivers. | -| **cache.query** | Cache transformation of a DQL query to its SQL counterpart. | -| **cache.result** | The result cache can be used to cache the results of your queries so you don't have to query the database or hydrate the data again after the first time. | - -### Gedmo - -This is an option for use with **Extensions** - -To use this option you must first install the extensions package: - -``` -require laravel-doctrine/extensions -``` -and follow the [installation instructions.](http://www.laraveldoctrine.org/docs/current/extensions/installation) diff --git a/docs/config-migrator.md b/docs/config-migrator.md deleted file mode 100644 index 37dce0da..00000000 --- a/docs/config-migrator.md +++ /dev/null @@ -1,91 +0,0 @@ -#Config File Migrator - -Laravel Doctrine provides a command to help migrate the configuration from another Laravel/Doctrine package to Laravel Doctrine's format. - -> This tool is meant to be used AS A STARTING POINT for converting your configuration. In most cases you will still need to inspect and modify the generated configuration to suite your needs. - -## Supported Packages - -| Package | Version | -|:----:|---| -| [atrauzzi/laravel-doctrine](https://github.com/atrauzzi/laravel-doctrine) | >= [dfef4ad](https://github.com/atrauzzi/laravel-doctrine/commit/dfef4ad87801a746a45d94d944a996498086a137) | -| [mitchellvanw/laravel-doctrine](https://github.com/mitchellvanw/laravel-doctrine) | >= 0.5.0 or [FoxxMD's Fork](https://github.com/FoxxMD/laravel-doctrine) | - - -## Converting an Existing Configuration - -You must have artisan installed in your project in order to use this command. - -From the commandline usage is the following: - -`php artisan doctrine:config:convert [author] [--source-file] [--dest-path]` - -| Flag |Description| -|:----:|---| -| `author` |The author of the package migrating from. Available authors are: [mitchellvanw](https://github.com/mitchellvanw/laravel-doctrine) & [atrauzzi](https://github.com/atrauzzi/laravel-doctrine) | -| `--source-file` |Path to your existing configuration file from the root dir of your project. If not provided defaults to `config/doctrine.php` | -| `--dest-path` |Path where the migrated configuration should be created. If not provided defaults to `config/` | - -If migration is successful the file `doctrine.generated.php` is created in the `dest-path` specified. - -## Writing A Template for a Configuration - -To create a new configuration file [blade templates](https://laravel.com/docs/master/blade) are used to create php code that is then rendered to a string and written to a file. Templates take in the original configuration as an array and output sections of the new configuration with the transformed values. - -To create a template follow the following steps: - -## Implement "ConfigurationMigrator" - -### Implement the interface - -First, create a new class that implements the interface [`LaravelDoctrine\ORM\ConfigMigrations`](https://github.com/laravel-doctrine/orm/blob/develop/src/Console/ConfigMigrations/ConfigurationMigrator.php) - -### Write templates for each section of the config - -**This is one way to use templates, but may not be the best for your scenario.** Use your discretion. - -For each section in the configuration write a function that takes in the original configuration (or section of it) and provides it as an argument to a blade template. - -**Example** - - /** - * Convert an entity manager section from mitchellvanw/laravel-doctrine to a string representation of a php array configuration for an entity manager for this project - * - * @param array $sourceArray - * @param boolean $isFork - * @return string - */ - public function convertManager($sourceArray, $isFork) - { - $results = $this->viewFactory->make('mitchell.manager', ['data' => $sourceArray, 'isFork' => $isFork])->render(); - $unescaped = html_entity_decode($results, ENT_QUOTES); - return $unescaped; - } - - Write the new section of the configuration using blade syntax to create patterns to iterate over (IE `foreach` over `entityManagers`). - -**Example** - - [ - 'meta' => '{{{ $isFork ? $data['metadata']['driver'] : 'annotations' }}}', - 'connection' => {{{ $isFork ? '\''.$data['connection'].'\'' : 'config("database.default")' }}}, - 'paths' => {{ var_export(ArrayUtil::get($data['metadata']['paths'], $data['metadata']), true) }}, - 'repository' => '{{{ ArrayUtil::get($data['repository'], \LaravelDoctrine\ORM\EntityRepository::class) }}}', - 'proxies' => [ - 'namespace' => {{{ isset($data['proxy']['namespace']) ? '\'' . $data['proxy']['namespace'] .'\'' : 'false' }}}, - 'path' => '{{{ ArrayUtil::get($data['proxy']['directory'], storage_path('proxies')) }}}', - 'auto_generate' => '{{{ ArrayUtil::get($data['proxy']['auto_generate'], env('DOCTRINE_PROXY_AUTOGENERATE', 'false')) }}}' - ], - 'events' => [ - 'listeners' => [], - 'subscribers' => [] - ], - 'filters' => [] - ] - - -**Use ["MitchellMigrator"](https://github.com/laravel-doctrine/orm/blob/master/src/Console/ConfigMigrations/MitchellMigrator.php) as a reference.** - -## Add Your Migrator to "ConvertConfigCommand" - -Finally, instantiate your Migrator and add a case for it in ["ConvertConfigCommand"](https://github.com/laravel-doctrine/orm/blob/master/src/Console/ConvertConfigCommand.php). diff --git a/docs/configuration.rst b/docs/configuration.rst new file mode 100644 index 00000000..73b64117 --- /dev/null +++ b/docs/configuration.rst @@ -0,0 +1,315 @@ +=========== +Config File +=========== + +This is an overview of all of the options provided in `~/config/doctrine.php` + + +Entity Manager +============== + +An entity manager (em) contains all of the information Doctrine needs +to understand, retrieve, and manipulate a set of entities. + +The name for the default entity manager is ``default``. This is configured +in the config file by the array key name under ``managers``. + +.. code-block:: php + + return [ + 'managers' => [ + 'default' => [ // This is the entity manager name + ... + ], + +Within the entity manager name array are configuration settings. These are + +* ``dev`` - defaults to the APP_DEBUG setting of the application. +* ``meta`` - The type of metadata configuration. Valid values are + ``attributes``, ``xml``, ``simplified_xml``, ``static_php``, ``php``. + The majority of configurations use ``attributes`` or ``xml`` and these + metadata configurtions are recommended. +* ``connection`` - This is the ``DB_CONNECTION`` variable from the ``.env`` + file by default. Connections are handled by Laravel. See the + ``database.php`` config file. +* ``namespaces`` - If your entities are not located in the configured app + namespace, you can specify a different one here. +* ``paths`` - The path(s) where the mapping configurations for your entities + are located. +* ``repository`` - An EntityRepository serves as a repository for entities + with generic as well as business specific methods for retrieving entities. +* ``decorator`` - A custom entity manager decorator to override the default. +* ``proxies.namespace`` - Namespace (if different) specified for proxy classes. +* ``proxies.path`` - The path where proxy classes should be generated. +* ``proxies.auto_generate`` - Should proxy classes be generated every time an + entity manager is created? Turn off for production. +* ``events.subscribers`` - Subscribers should implement ``Doctrine\Common\EventSubscriber`` +* ``events.listeners`` - Key should be event type. e.g. ``Doctrine\ORM\Events::onFlush``. + Value should be the listener class +* ``filters`` - Filter system that allows the developer to add SQL to the + conditional clauses of queries, regardless the place where the SQL is generated + + +Multiple Entity Managers +======================== + +Multiple entity managers are supported in the configuration. + +To use more than one entity manager, create another entry in the `managers` +array. + +.. code-block:: php + + return [ + 'managers' => [ + 'default' => [ // This is the first entity manager configuration + ... + ], + 'second' => [ // This is the second entity manager configuration + ... + ] + + +Example configuration + +.. code-block:: php + 'managers' => [ + 'default' => [ + 'dev' => env('APP_DEBUG'), + 'meta' => env('DOCTRINE_METADATA', 'attributes'), + 'connection' => env('DB_CONNECTION', 'mysql'), + 'namespaces' => [ + 'App' + ], + 'paths' => [ + base_path('app') + ], + 'repository' => Doctrine\ORM\EntityRepository::class, + 'proxies' => [ + 'namespace' => false, + 'path' => storage_path('proxies'), + 'auto_generate' => env('DOCTRINE_PROXY_AUTOGENERATE', false) + ], + 'events' => ... + 'filters' => ... + ] + ] + +Namespace Alias +=============== + +To use namespace alias, you just have to specify then as key of each namespace. + +Example: + +.. code-block:: php + + 'managers' => [ + 'default' => [ + ... + 'connection' => env('DB_CONNECTION', 'mysql'), + 'namespaces' => [ + 'Foo' => 'App\Model\Foo\Entities', + 'Bar' => 'App\Model\Bar\Entities', + ], + 'paths' => [ + base_path('app') + ], + ... + ] + ] + + +Whenever you need to specify entities in these namespaces, you can simple +use the alias as follow: + +.. code-block:: sql + + SELECT f FROM Foo:SomeEntity + +or + +.. code-block:: php + + EntityManager::getRepository('Bar:SomeEntity'); + + +Extensions +========== + +Extensions can be enabled by adding them to this array. They provide additional +functionality Entities (Timestamps, Loggable, etc.) + +To use the extensions in this sample you must install the extensions package. + +.. code-block:: bash + + composer require laravel-doctrine/extensions + +and follow the +`installation instructions `_. + +.. code-block:: php + + 'extensions' => [ + //LaravelDoctrine\ORM\Extensions\TablePrefix\TablePrefixExtension::class, + //LaravelDoctrine\Extensions\Timestamps\TimestampableExtension::class, + //LaravelDoctrine\Extensions\SoftDeletes\SoftDeleteableExtension::class, + //LaravelDoctrine\Extensions\Sluggable\SluggableExtension::class, + //LaravelDoctrine\Extensions\Sortable\SortableExtension::class, + //LaravelDoctrine\Extensions\Tree\TreeExtension::class, + //LaravelDoctrine\Extensions\Loggable\LoggableExtension::class, + //LaravelDoctrine\Extensions\Blameable\BlameableExtension::class, + //LaravelDoctrine\Extensions\IpTraceable\IpTraceableExtension::class, + //LaravelDoctrine\Extensions\Translatable\TranslatableExtension::class + ], + + +Custom Types +============ + +Custom types are classes that allow Doctrine to marshal data to/from the data +source in a custom format. + +To register a custom type simple add the class to this list. +`For more information on custom types refer to the Doctrine documentation `_. + + +Custom Functions +================ + +These are classes that extend the functionality of Doctrine's DQL language. +More information on what functions are available +`at the repository `_ + +To use the extensions in this sample you must install the extensions package: + +.. code-block:: bash + + doctrine require laravel-doctrine/extensions + +and follow the `installation instructions `_. + +If you include the `BeberleiExtensionsServiceProvider` all custom functions will +automatically be registered. + +To add a function simply add it to the correct list using this format: +``'FUNCTION_NAME' => 'Path\To\Class'`` + + +.. code-block:: php + + /* + |-------------------------------------------------------------------------- + | DQL custom datetime functions + |-------------------------------------------------------------------------- + */ + 'custom_datetime_functions' => [], + /* + |-------------------------------------------------------------------------- + | DQL custom numeric functions + |-------------------------------------------------------------------------- + */ + 'custom_numeric_functions' => [], + /* + |-------------------------------------------------------------------------- + | DQL custom string functions + |-------------------------------------------------------------------------- + */ + 'custom_string_functions' => [], + + +Custom Hydration Modes +====================== + +This option enables you to register your Hydrator classes to use as custom +hydration modes. For more information about custom hydration modes see +`Doctrine documentation `_. + +To register custom hydrator, add it to the list in following format: + +``'hydrationModeName' => MyHydrator::class`` + +.. code-block:: php + + /* + |-------------------------------------------------------------------------- + | Register custom hydrators + |-------------------------------------------------------------------------- + */ + 'custom_hydration_modes' => [ + 'hydrationModeName' => MyHydrator::class, + ], + + +Logger +====== + +Enable logging of Laravel Doctrine and Doctrine by using the logger +functionality. + +Available loggers + +* ``LaravelDoctrine\ORM\Loggers\LaravelDebugbarLogger`` +* ``LaravelDoctrine\ORM\Loggers\ClockworkLogger`` +* ``LaravelDoctrine\ORM\Loggers\FileLogger`` + +.. code-block:: php + + 'logger' => env('DOCTRINE_LOGGER', false), + + +Cache +===== + +Caches will be used to cache metadata, results, and queries. + +Available cache providers + +* apc +* array +* file +* memcached +* redis + +Config settings +--------------- + +* ``cache.default`` - The default cache provider to use. +* ``cache.namespace`` - Will add namespace to the cache key. This is useful if + you need extra control over handling key names collisions in your + cache solution. +* ``cache.second_level`` - The Second Level Cache is designed to reduce the + amount of necessary database access. It sits between your application and + the database to avoid the number of database hits as much as possible. When + turned on, entities will be first searched in cache and if they are not + found, a database query will be fired an then the entity result will be + stored in a cache provider. When used, READ_ONLY is mostly used. ReadOnly + cache can do reads, inserts and deletes, and cannot perform updates. +* ``cache.metadata`` - Your class metadata can be parsed from a few different + sources like YAML, XML, Annotations, etc. Instead of parsing this information + on each request we should cache it using one of the cache drivers. +* ``cache.query`` - Cache transformation of a DQL query to its SQL counterpart. +* ``cache.result`` - The result cache can be used to cache the results of your +* queries so you don't have to query the database or hydrate the data again +* after the first time. + +Gedmo +===== + +This is an option for use with ``Extensions``. + +To use this option you must first install the extensions package + +.. code-block:: bash + + composer require laravel-doctrine/extensions + +and follow the +`installation instructions `_. + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/connections.md b/docs/connections.md deleted file mode 100644 index 14e77964..00000000 --- a/docs/connections.md +++ /dev/null @@ -1,31 +0,0 @@ -# Connections - -Out of the box the database connections configured in `database.php` config are supported: - -* mysql -* sqlite -* pqsql -* sqlsrv -* oci8 - -Please note that read/write connections are supported as well. Check out [laravel documentation](https://laravel.com/docs/database#read-and-write-connections) for more details. - -By simply changing the `DB_CONNECTION` env variable you swap the database connection for Doctrine as well. -The additional settings per connection are applied by default. - -## Extending or Adding Connections Drivers - -You can replace existing connection drivers or add custom drivers using the `LaravelDoctrine\ORM\Configuration\Connections\ConnectionManager`. Should return an array of parameters. - -```php -public function boot(ConnectionManager $connections) { - $connections->extend('myDriver', function(array $settings, \Illuminate\Contracts\Container\Container $container) { - return [ - 'driver' => 'driver', - 'host' => ... - ]; - }); -} -``` - -You can find the available connection parameters inside the Doctrine documentation: http://doctrine-dbal.readthedocs.org/en/latest/reference/configuration.html diff --git a/docs/connections.rst b/docs/connections.rst new file mode 100644 index 00000000..3da63c3e --- /dev/null +++ b/docs/connections.rst @@ -0,0 +1,51 @@ +=========== +Connections +=========== + +Database connections configured in ``config/database.php`` are supported: + +* mysql +* sqlite +* pqsql +* sqlsrv +* oci8 + +Please note that read/write connections are supported as well. See the +`Laravel documentation `_ +for more details. + +Changing the ``DB_CONNECTION`` environment variable swaps the database +connection for Doctrine as well. +The additional settings per connection are applied by default. + + +Extending or Adding Connections Drivers +======================================= + +You can replace existing connection drivers or add custom drivers using the +``LaravelDoctrine\ORM\Configuration\Connections\ConnectionManager``. +This should return an array of parameters. + +.. code-block:: php + + use LaravelDoctrine\ORM\Configuration\Connections\ConnectionManager; + use Illuminate\Contracts\Container\Container; + + public function boot(ConnectionManager $connections): void + { + $connections->extend('myDriver', function(array $settings, Container $container) { + return [ + 'driver' => 'driver', + 'host' => ... + ]; + }); + } + +You can find the available connection parameters inside the +`Doctrine documentation `_. + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/console.md b/docs/console.md deleted file mode 100644 index 33cd7b59..00000000 --- a/docs/console.md +++ /dev/null @@ -1,16 +0,0 @@ -# Console - -This package offers a bunch of artisan commands to speed up development: - -Artisan command | Description ---- | --- -`doctrine:clear:metadata:cache` | Clear all metadata cache of the various cache drivers. -`doctrine:clear:query:cache`|Clear all query cache of the various cache drivers. -`doctrine:clear:result:cache`|Clear all result cache of the various cache drivers. -`doctrine:ensure:production`|Verify that Doctrine is properly configured for a production environment. -`doctrine:generate:proxies`|Generates proxy classes for entity classes. -`doctrine:info`|Show basic information about all mapped entities. -`doctrine:schema:create`|Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output. -`doctrine:schema:drop`|Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output. -`doctrine:schema:update`|Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata. -`doctrine:schema:validate`|Validate the mapping files. diff --git a/docs/console.rst b/docs/console.rst new file mode 100644 index 00000000..02d6e7c0 --- /dev/null +++ b/docs/console.rst @@ -0,0 +1,26 @@ +========================== +Console (Artisan Commands) +========================== + +Many artisan commands are included in this repository. + +* ``doctrine:clear:metadata:cache`` - Clear all metadata cache of the various + cache drivers. +* ``doctrine:clear:query:cache`` - Clear all query cache of the various + cache drivers. +* ``doctrine:clear:result:cache`` - Clear all result cache of the various + cache drivers. +* ``doctrine:generate:proxies`` - Generates proxy classes for entity classes. +* ``doctrine:info`` - Show basic information about all mapped entities. +* ``doctrine:schema:create`` - Processes the schema and either create it + directly on EntityManager Storage Connection or generate the SQL output. +* ``doctrine:schema:drop`` - Drop the complete database schema of EntityManager + Storage Connection or generate the corresponding SQL output. +* ``doctrine:schema:update`` - Executes (or dumps) the SQL needed to update + the database schema to match the current mapping metadata. +* ``doctrine:schema:validate`` - Validate the mapping files. + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/contributions.md b/docs/contributions.md deleted file mode 100644 index c02f7fe1..00000000 --- a/docs/contributions.md +++ /dev/null @@ -1,49 +0,0 @@ -# Contribution Guide - -- [Bug Reports](#bug-reports) -- [Core Development Discussion](#core-development-discussion) -- [Which Branch?](#which-branch) -- [Coding Style](#coding-style) - - -## Bug Reports - -To encourage active collaboration, we strongly encourages pull requests, not just bug reports. "Bug reports" may also be sent in the form of a pull request containing a failing test. - -However, if you file a bug report, your issue should contain a title and a clear description of the issue. You should also include as much relevant information as possible and a code sample that demonstrates the issue. The goal of a bug report is to make it easy for yourself - and others - to replicate the bug and develop a fix. - -Remember, bug reports are created in the hope that others with the same problem will be able to collaborate with you on solving it. Do not expect that the bug report will automatically see any activity or that others will jump to fix it. Creating a bug report serves to help yourself and others start on the path of fixing the problem. - -The Laravel Doctrine source code is managed on Github, and there are repositories for each of the Laravel Doctrine projects: - -- [Laravel Doctrine ORM](https://github.com/laravel-doctrine/orm) -- [Laravel Doctrine ACL](https://github.com/laravel-doctrine/acl) -- [Laravel Doctrine Extensions](https://github.com/laravel-doctrine/extensions) -- [Laravel Doctrine Migrations](https://github.com/laravel-doctrine/migrations) -- [Laravel Doctrine Documentation](https://github.com/laravel-doctrine/docs) -- [Laravel Doctrine Website](https://github.com/laravel-doctrine/laraveldoctrine.org) - - -## Core Development Discussion - -Discussion regarding bugs, new features, and implementation of existing features takes place in the channel linked to the package of the [Laravel Doctrine Slack](http://slack.laraveldoctrine.org) Slack team. - - -## Which Branch? - -**All** bug fixes should be sent to the latest stable branch. Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. - -**Minor** features that are **fully backwards compatible** with the current Laravel Doctrine release may be sent to the latest stable branch. - -**Major** new features should always be sent to the `master` branch, which contains the upcoming Laravel Doctrine release. - -If you are unsure if your feature qualifies as a major or minor, please ask help in the `#general` channel of the [Laravel Doctrine Slack](http://slack.laraveldoctrine.org) Slack team. - -It's always a good idea to back your Pull Request with tests, proving your implementation works and gives an idea of what your code does. Always run the tests locally to see if you didn't break any existing code. - - -## Coding Style - -Laravel Doctrine follows the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding standard and the [PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md) autoloading standard. - -A config file for [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) is included. Every PR will be analyzed for PSR-2 and PSR-4 by [StyleCI](https://styleci.io/). diff --git a/docs/default-table-options.md b/docs/default-table-options.rst similarity index 53% rename from docs/default-table-options.md rename to docs/default-table-options.rst index 55174962..7ca366e5 100644 --- a/docs/default-table-options.md +++ b/docs/default-table-options.rst @@ -1,12 +1,14 @@ -# Default Table Options +===================== +Default Table Options +===================== You can set default table options for your database connections. -Modify your `database.php` configuration file to include the `defaultTableOptions` key +Modify your ``database.php`` configuration file to include the +``defaultTableOptions`` key + +.. code-block:: php -# Example -for a MySQL databse -```php ... 'mysql' => [ 'driver' => 'mysql', @@ -26,9 +28,18 @@ for a MySQL databse 'engine' => null, ], ... -``` -Property | Explaination ----- | ---- -charset | Tables will be created with `CHARACTER SET utf8` by default *Even If* your database is set up with it's own default character set. Setting this property will ensure new tables are created with the given encoding -collate | As above, this must be edited if you with your collation to be anything other than utf8_unicode_ci + +* charset - Tables will be created with `CHARACTER SET utf8` by default + unless otherwise specified in your metadata. + **Even If** your database is set up with it's own default character set. + Setting this property will ensure new tables are created with the given + encoding +* collate - As above, this must be edited if you with your collation to be + anything other than utf8_unicode_ci + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/doctrine-manager.md b/docs/doctrine-manager.md deleted file mode 100644 index 99da8c11..00000000 --- a/docs/doctrine-manager.md +++ /dev/null @@ -1,54 +0,0 @@ -# Accessing Entity Managers - -Laravel Doctrine uses `DoctrineManager` to provide an easy method of hooking into the internals of an Entity Manager for more advanced configuration than is possible with just a configuration file. - -It provides access to three facets of Doctrine: - -* [Doctrine\ORM\Configuration](http://www.doctrine-project.org/api/dbal/2.1/class-Doctrine.DBAL.Configuration.html) -* [Doctrine\DBAL\Connection](http://www.doctrine-project.org/api/dbal/2.1/class-Doctrine.DBAL.Connection.html) -* [Doctrine\Common\EventManager](http://www.doctrine-project.org/api/common/2.2/class-Doctrine.Common.EventManager.html) - -These objects are accessed **per Entity Manager** using the name configured for that EM in `doctrine.php` - -## Using DoctrineManager - -Boilerplate example of `DoctrineManager` using facade `LaravelDoctrine\ORM\Facades\Doctrine` - -```php -Doctrine::extend('myManager', function(Configuration $configuration, Connection $connection, EventManager $eventManager) { - //modify and access settings as is needed -}); -``` - -Using dependency injection in `boot()` of a ServiceProvider - -```php -public function boot(DoctrineManager $manager) { - $manager->extend('myManager', function(Configuration $configuration, Connection $connection, EventManager $eventManager) { - //modify and access settings as is needed - }); -} -``` - -## Implementing Your Own Extender - -Additionally, you can write your own custom manager by implementing `LaravelDoctrine\ORM\DoctrineExtender` - -```php -class MyDoctrineExtender implements DoctrineExtender -{ - /** - * @param Configuration $configuration - * @param Connection $connection - * @param EventManager $eventManager - */ - public function extend(Configuration $configuration, Connection $connection, EventManager $eventManager) - { - //your extending code... - } -} -``` - -```php -$manager->extend('myManager', MyDoctrineExtender::class); -``` diff --git a/docs/doctrine-manager.rst b/docs/doctrine-manager.rst new file mode 100644 index 00000000..c1f0b5e3 --- /dev/null +++ b/docs/doctrine-manager.rst @@ -0,0 +1,72 @@ +============================================== +Doctrine Manager and Accessing Entity Managers +============================================== + +Accessing Entity Managers +========================= + +Laravel Doctrine uses ``DoctrineManager`` to provide an easy method of hooking +into the internals of an Entity Manager for more advanced configuration than +is possible with just a configuration file. + +It provides access to three facets of Doctrine: + +* `Doctrine\ORM\Configuration `_ +* `Doctrine\DBAL\Connection `_ +* `Doctrine\Common\EventManager `_ + +These objects are accessed **per Entity Manager** using the name configured +for that EM in ``doctrine.php`` + +Using DoctrineManager +===================== + +Boilerplate example of ``DoctrineManager`` using facade ``LaravelDoctrine\ORM\Facades\Doctrine`` + +.. code-block:: php + + Doctrine::extend('myManager', function(Configuration $configuration, Connection $connection, EventManager $eventManager) { + //modify and access settings as is needed + }); + + +Using dependency injection in ``boot()`` of a ServiceProvider + +.. code-block:: php + + public function boot(DoctrineManager $manager) { + $manager->extend('myManager', function(Configuration $configuration, Connection $connection, EventManager $eventManager) { + //modify and access settings as is needed + }); + } + + +Implementing Your Own Extender +============================== + +Additionally, you can write your own custom manager by implementing +``LaravelDoctrine\ORM\DoctrineExtender`` + +.. code-block:: php + + class MyDoctrineExtender implements DoctrineExtender + { + /** + * @param Configuration $configuration + * @param Connection $connection + * @param EventManager $eventManager + */ + public function extend(Configuration $configuration, Connection $connection, EventManager $eventManager) + { + //your extending code... + } + } + + + $manager->extend('myManager', MyDoctrineExtender::class); + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/entities.md b/docs/entities.md deleted file mode 100644 index 214d97a0..00000000 --- a/docs/entities.md +++ /dev/null @@ -1,171 +0,0 @@ -# Entities - -Out of the box this package uses the default Laravel connection from the `.env` file `DB_CONNECTION`, which means that you are ready to start fetching and persisting. - -```php -addTheory( - new Theory('Theory of relativity') -); - -EntityManager::persist($scientist); -EntityManager::flush(); -``` - -Unlike Eloquent, Doctrine is not an Active Record pattern, but a Data Mapper pattern. Every Active Record model extends a base class (with all the database logic), which has a lot of overhead and dramatically slows down your application with thousands or millions of records. -Doctrine entities don't extend any class, they are just regular PHP classes with properties and getters and setters. -Generally the properties are protected or private, so they only can be accessed through getters and setters. - -The domain/business logic is completely separated from the persistence logic. -This means we have to tell Doctrine how it should map the columns from the database to our Entity class. In this example we are using annotations. Other possiblities are YAML, XML or PHP arrays. - -Entities are objects with identity. Their identity has a conceptual meaning inside your domain. In an application each article has a unique id. You can uniquely identify each article by that id. - -Relations are stored in `ArrayCollection`. It's advised to always set this default value in the constructor. `$this->theories = new ArrayCollection`. -You can easily add on new relations with `->add()`, remove them with `->removeElement()` or check if the relation is already defined with `->contains()` - -The `Scientist` entity used in the example above looks like this when using annotations for the meta data. - -```php -firstname = $firstname; - $this->lastname = $lastname; - - $this->theories = new ArrayCollection; - } - - public function getId() - { - return $this->id; - } - - public function getFirstname() - { - return $this->firstname; - } - - public function getLastname() - { - return $this->lastname; - } - - public function addTheory(Theory $theory) - { - if(!$this->theories->contains($theory)) { - $theory->setScientist($this); - $this->theories->add($theory); - } - } - - public function getTheories() - { - return $this->theories; - } -} -``` - -The related `Theory` entity would look like this: - -```php -title = $title; - } - - public function getId() - { - return $this->id; - } - - public function getTitle() - { - return $this->title; - } - - public function setScientist(Scientist $scientist) - { - $this->scientist = $scientist; - } - - public function getScientist() - { - return $this->scientist; - } -} -``` diff --git a/docs/entities.rst b/docs/entities.rst new file mode 100644 index 00000000..b1011c5b --- /dev/null +++ b/docs/entities.rst @@ -0,0 +1,169 @@ +======== +Entities +======== + +Out of the box, this package uses the default Laravel connection from +the ``.env`` file ``DB_CONNECTION``, which means that you are ready to start +fetching and persisting. + +.. code-block:: php + + $scientist = new Scientist( + 'Albert', + 'Einstein' + ); + + $scientist->addTheory( + new Theory('Theory of relativity') + ); + + EntityManager::persist($scientist); + EntityManager::flush(); + +Unlike Eloquent, Doctrine is **not** an Active Record pattern, but a +Data Mapper pattern. Every Active Record model extends a base class +(with all the database logic), which has a lot of overhead and dramatically +slows down your application with thousands or millions of records. +Doctrine entities don't extend any class, they are just regular PHP classes +with properties and getters and setters. +Generally the properties are protected or private, so they only can be accessed +through getters and setters. + +The domain/business logic is completely separated from the persistence logic. +This means we have to tell Doctrine how it should map the columns from the +database to our Entity class. In this example we are using annotations. +Other possiblities are YAML, XML or PHP arrays. + +Entities are objects with identity. Their identity has a conceptual meaning +inside your domain. In an application each article has a unique id. You can +uniquely identify each article by that id. + +Relations are stored in ``ArrayCollection``. It's advised to always set this +default value in the constructor. ``$this->theories = new ArrayCollection()``. +You can easily add on new relations with ``->add()``, remove them with +``->removeElement()`` or check if the relation is already defined with +``->contains()`` + +The ``Scientist`` entity used in the example above looks like this when using +annotations for the meta data. + + Coding note: Thanks to property hooks in PHP 8.4, getters and setters may + no longer be necessary. + +.. code-block:: php + + use Doctrine\ORM\Mapping as ORM; + use Doctrine\Common\Collections\ArrayCollection; + use Doctrine\Common\Collections\Collection; + + #[ORM\Entity] + class Scientist + { + #[ORM\Id] + #[ORM\Column(type: "integer")] + #[ORM\GeneratedValue(strategy: "AUTO")] + private int $id; + + #[ORM\Column(type: "string", nullable: false)] + private string $firstname; + + #[ORM\Column(type: "string", nullable: false)] + private string $lastname; + + #[ORM\OneToMany(targetEntity: \Theory::class, mappedBy: "scientist")] + private Collection $theories; + + /** + * @param $firstname + * @param $lastname + */ + public function __construct() + { + $this->theories = new ArrayCollection(); + } + + public function getId(): int + { + return $this->id; + } + + public function getFirstname(): string + { + return $this->firstname; + } + + public function getLastname(): string + { + return $this->lastname; + } + + public function addTheory(Theory $theory): self + { + if (! $this->theories->contains($theory)) { + $theory->setScientist($this); + $this->theories->add($theory); + } + + return $this; + } + + public function getTheories(): Collection + { + return $this->theories; + } + } + + +The related `Theory` entity would look like this: + +.. code-block:: php + + use Doctrine\ORM\Mapping as ORM; + + #[ORM\Entity] + class Theory + { + #[ORM\Id] + #[ORM\Column(type: "integer")] + #[ORM\GeneratedValue(strategy: "AUTO")] + private int $id; + + #[ORM\Column(type: "string", nullable: false)] + private string $title; + + #[ORM\ManyToOne(targetEntity: \Scientist::class, inversedBy: "theories")] + #[ORM\JoinColumn(name: "scientist_id", referencedColumnName: "id", nullable: false)] + private Scientist $scientist; + + public function __construct() + { + } + + public function getId(): int + { + return $this->id; + } + + public function getTitle(): string + { + return $this->title; + } + + public function setScientist(Scientist $scientist): self + { + $this->scientist = $scientist; + + return $this; + } + + public function getScientist(): Scientist + { + return $this->scientist; + } + } + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/entity-manager.md b/docs/entity-manager.md deleted file mode 100644 index 832d990c..00000000 --- a/docs/entity-manager.md +++ /dev/null @@ -1,114 +0,0 @@ -#Entity Manager - -The EntityManager is the central access point to ORM functionality. It can be used to find, persist, flush and remove entities. - -## Using the EntityManager - -#### Facade - -You can use the facade to access the `EntityManager` methods - -```php -EntityManager::flush(); -``` - -#### Container - -```php -app('em'); -app('Doctrine\ORM\EntityManagerInterface'); -``` - -#### Dependency injection - -```php -em = $em; - } -} -``` - -#### Multiple connections - -If you are using multiple connections `EntityManagerInterface` will return the default connection. If you want to have control over which EnityManager you want, you'll have to inject `Doctrine\Common\Persistence\ManagerRegistry` - -```php -use Doctrine\Common\Persistence\ManagerRegistry; - -class ExampleController extends Controller -{ - protected $em; - - public function __construct(ManagerRegistry $em) - { - $this->em = $em->getManager('otherConnection'); - } -} -``` - -## Finding entities - -> Note: for making the examples more expressive, we will use the facade. However I do recommend to leverage dependency injection as much as possible. This makes mocking the EntityManager in your tests a lot easier. - -Entities are objects with identity. Their identity has a conceptual meaning inside your domain. In a CMS application each article has a unique id. You can uniquely identify each article by that id. - -In the example underneath, the Article entity is fetched from the entity manager twice, but was modified after the first find. Doctrine2 keeps track of all those changes. This pattern is called `Identity Map pattern`, which means that Doctrine keeps a map of each entity and ids that have been retrieved per request and keeps return the same instances on every find. - -`$article` and `$article1` will be identical, eventhough we haven't persisted the changes to `$article` to the database yet. - -```php -$article = EntityManager::find('App\Entities\Article', 1); -$article->setTitle('Different title'); - -$article2 = EntityManager::find('App\Entities\Article', 1); - -if ($article === $article2) { - echo "Yes we are the same!"; -} -``` - -### Persisting - -By passing the entity through the `persist` method of the EntityManager, that entity becomes MANAGED, which means that its persistence is from now on managed by an EntityManager. As a result the persistent state of such an entity will subsequently be properly synchronised with the database when `EntityManager::flush()` is invoked. - -> Note: `persist()` doesn't do any `INSERT` queries. - -```php -$article = new Article; -$article->setTitle('Let\'s learn about persisting'); - -EntityManager::persist($article); -EntityManager::flush(); -``` - -### Flushing - -Whenever you have changed the state of an Entity (updated, created, removed, ...) you will have to flush the entity manager to persist these changes to the database. - -```php -// Flush all changes -EntityManager::flush(); - -// Only flush changes of given entity -EntityManager::flush($article); -``` - -### Removing - -An entity can be removed from persistent storage by passing it to the `remove($entity)` method. By applying the remove operation on some entity, that entity becomes REMOVED, which means that its persistent state will be deleted once `flush()` is invoked. - -```php -EntityManager::remove($article); -EntityManager::flush(); -``` - -More information about the entity manager: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/working-with-objects.html diff --git a/docs/entity-manager.rst b/docs/entity-manager.rst new file mode 100644 index 00000000..7c729adb --- /dev/null +++ b/docs/entity-manager.rst @@ -0,0 +1,156 @@ +============== +Entity Manager +============== + +The EntityManager is the central access point to ORM functionality. It can be +used to find, persist, flush and remove entities. + +Using the EntityManager +======================= + +Facade +------ + +You can use the ``EntityManager`` facade to access the `EntityManager` methods + +.. code_block:: php + + EntityManager::flush(); + + +Container +--------- + +.. code-block:: php + + app('em'); // alias + app('Doctrine\ORM\EntityManagerInterface'); + + +Dependency Injection +-------------------- + +.. code-block:: php + + use Doctrine\ORM\EntityManagerInterface; + + class ExampleController extends Controller + { + public function __construct(protected EntityManagerInterface $em) + { + } + } + + +Multiple connections +-------------------- + +If you are using multiple managers, ``EntityManagerInterface`` will only return +the default connection. If you want to have control over which EnityManager +you want, you'll have to inject ``Doctrine\Common\Persistence\ManagerRegistry`` + + +.. code-block:: php + use Doctrine\Common\Persistence\ManagerRegistry; + + class ExampleController extends Controller + { + protected $em; + + public function __construct(ManagerRegistry $em) + { + $this->em = $em->getManager('otherConnection'); + } + } + + +Finding entities +---------------- + + Note: for making the examples more expressive, we will use the facade. + However I do recommend to leverage dependency injection as much as possible. + This makes mocking the EntityManager in your tests a lot easier. + +Entities are objects with identity. Their identity has a conceptual meaning +inside your domain. In a CMS application each article has a unique id. You +can uniquely identify each article by that id. + +In the example below, the Article entity is fetched from the entity manager +twice, but was modified after the first find. Doctrine2 keeps track of all +those changes. This pattern is called `Identity Map pattern`, which means +that Doctrine keeps a map of each entity and ids that have been retrieved +per request and keeps return the same instances on every find. + +``$article`` and ``$article1`` will be identical, eventhough we haven't +persisted the changes to ``$article`` to the database yet. + +.. code-block:: php + + $article = EntityManager::find('App\Entities\Article', 1); + $article->setTitle('Different title'); + + $article2 = EntityManager::find('App\Entities\Article', 1); + + if ($article === $article2) { + echo "Yes we are the same!"; + } + + +Persisting +========== + +By passing the entity through the ``persist`` method of the EntityManager, +that entity becomes MANAGED, which means that its persistence is from now +on managed by an EntityManager. As a result the persistent state of such +an entity will subsequently be properly synchronised with the database +when ``EntityManager::flush()`` is invoked. + + Note: ``persist()`` doesn't do any ``INSERT`` queries. + +.. code-block:: php + + $article = new Article; + $article->setTitle('Let\'s learn about persisting'); + + EntityManager::persist($article); + EntityManager::flush(); + + +Flushing +======== + +Whenever you have changed the state of an Entity +(updated, created, removed) you will have to flush the entity manager to +persist these changes to the database. + +.. code-block:: php + + // Flush all changes + EntityManager::flush(); + + // Only flush changes of given entity + EntityManager::flush($article); + + +Removing (deleting) +=================== + +An entity can be removed from persistent storage by passing it to the +``remove($entity)`` method. By applying the remove operation on some entity, +that entity becomes REMOVED, which means that its persistent state will be +deleted once ``flush()`` is invoked. + +.. code-block:: php + + EntityManager::remove($article); + EntityManager::flush(); + + +More information about the entity manager directly from Doctrine: +https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/footer.rst b/docs/footer.rst new file mode 100644 index 00000000..75729061 --- /dev/null +++ b/docs/footer.rst @@ -0,0 +1,6 @@ + +---------- + +This is documentation for +`laravel-doctrine/orm `_. +Please add your ★ star to the project. diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 19135588..00000000 --- a/docs/index.md +++ /dev/null @@ -1,27 +0,0 @@ -- Prologue - - [Introduction](/docs/{{version}}/orm/introduction) - - [Upgrade Guide](/docs/{{version}}/orm/upgrade) - - [Contribution Guide](/docs/{{version}}/orm/contributions) -- Setup - - [Laravel](/docs/{{version}}/orm/installation) - - [Lumen](/docs/{{version}}/orm/lumen) -- The Basics - - [Entities](/docs/{{version}}/orm/entities) - - [Meta Data](/docs/{{version}}/orm/meta-data) - - [Entity Manager](/docs/{{version}}/orm/entity-manager) - - [Multiple Connections](/docs/{{version}}/orm/multiple-connections) - - [Repositories](/docs/{{version}}/orm/repositories) - - [Console](/docs/{{version}}/orm/console) -- Configuration - - [Configuration File Overview](/docs/{{version}}/orm/config-file) - - [Accessing Entity Managers](/docs/{{version}}/orm/doctrine-manager) - - [Extending Connections](/docs/{{version}}/orm/connections) - - [Extending MetaData](/docs/{{version}}/orm/meta-data-configuration) - - [Extending Caching](/docs/{{version}}/orm/caching) - - [Troubleshooting](/docs/{{version}}/orm/troubleshooting) -- Services - - [Authentication](/docs/{{version}}/orm/auth) - - [Password resets](/docs/{{version}}/orm/passwords) - - [Testing](/docs/{{version}}/orm/testing) - - [Validation](/docs/{{version}}/orm/validation) - - [Notifications](/docs/{{version}}/orm/notifications) diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..72cc9cc7 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,74 @@ +==================== +Laravel Doctrine ORM +==================== + +.. image:: banner.png + :align: center + :scale: 25 % + +An integration library for Laravel and Doctrine ORM. +Version 3 of this library supports Laravel 10+, +Doctrine ORM ^3.0, and Doctrine DBAL ^4.0. + + +Introduction +============ + +Doctrine 2 is an object-relational mapper (ORM) for PHP that provides +transparent persistence for PHP objects. It uses the Data Mapper pattern at +the heart, aiming for a complete separation of your domain/business logic +from the persistence in a relational database management system. + +The benefit of Doctrine for the programmer is the ability to focus on the +object-oriented business logic and worry about persistence only as a +secondary problem. This doesn’t mean persistence is downplayed by Doctrine 2. +However, it is our belief that there are considerable benefits for +object-oriented programming if persistence and entities are seperate. + +.. toctree:: + + :caption: Table of Contents + + install + entities + metadata + entity-manager + multiple-connections + repositories + console + + configuration + doctrine-manager + connections + caching + troubleshooting + + auth + passwords + testing + validation + notifications + pagination + + +Features +-------- + +* Easy configuration +* Pagination +* Preconfigured metadata, connections and caching +* Extendable: extend or add your own drivers for metadata, connections or cache +* Change metadata, connection or cache settings easy with a resolved hook +* Annotations, yaml, xml, config and static php meta data mappings +* Multiple entity managers and connections +* Laravel naming strategy +* Simple authentication implementation +* Password reminders implementation +* Doctrine console commands +* DoctrineExtensions supported +* Timestamps, Softdeletes and TablePrefix listeners + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/install.rst b/docs/install.rst new file mode 100644 index 00000000..52ead0ca --- /dev/null +++ b/docs/install.rst @@ -0,0 +1,86 @@ +======= +Install +======= + +Installation of this module uses composer. For composer documentation, please +refer to `getcomposer.org `_ :: + + $ composer require laravel-doctrine/orm + +To publish the config use: + +.. code-block:: bash + + php artisan vendor:publish --tag="config" --provider="LaravelDoctrine\ORM\DoctrineServiceProvider" + +Thanks to Laravel auto package discovery, the ServiceProvider and Facades are +automatically registered. However they can still be manually registered if +required (see below). + + +Environment Variables +===================== + +Environment variables used inside the config + +* ``DOCTRINE_CACHE`` +* ``DOCTRINE_LOGGER`` +* ``DOCTRINE_METADATA`` +* ``DOCTRINE_PROXY_AUTOGENERATE`` + + +Application Folder Structure +============================ + +Doctrine entities do not belong in the ``Model`` directory. +Because Eloquent is a part of the Laravel Framework, you will need a +directory structure just for Doctrine that is flexible enough to accomodate +two ORMs. + +.. code-block:: bash + + ~/app/ORM/Doctrine + ~/app/ORM/Doctrine/Entity + ~/app/ORM/Doctrine/Repository + ~/app/ORM/Doctrine/Subscriber + ~/app/ORM/Doctrine/Listener + +If you are using both Eloquent and Doctrine together in an application, it is +suggested you modify your directory structure to accomodate both in a logical +way. + +.. code-block:: bash + + ~/app/ORM/Eloquent + ~/app/ORM/Eloquent/Models + +Change the ``config/doctrine.php`` file paths + +.. code-block:: php + + 'paths' => [ + base_path('app/ORM/Doctrine/Entity'), + ], + + +Manual registration +=================== + +After updating composer, add the ServiceProvider to the providers +array in ``config/app.php`` + +.. code-block:: php + LaravelDoctrine\ORM\DoctrineServiceProvider::class, + +Optionally, you can register the EntityManager, Registry and/or Doctrine facades + +.. code-block:: php + 'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class, + 'Registry' => LaravelDoctrine\ORM\Facades\Registry::class, + 'Doctrine' => LaravelDoctrine\ORM\Facades\Doctrine::class, + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst diff --git a/docs/installation.md b/docs/installation.md deleted file mode 100644 index 1204f128..00000000 --- a/docs/installation.md +++ /dev/null @@ -1,52 +0,0 @@ -# Installation in Laravel 6+ - - Laravel | Laravel Doctrine -:---------|:---------- - 6.* | ~1.5 - 7.* | ~1.6 - 8.* | ~1.7 - 9.* | ~1.8 - -Install this package with composer: - -``` -composer require laravel-doctrine/orm -``` - -Thanks to Laravel auto package discovery feature, the ServiceProvider and Facades are automatically registered. -However they can still be manually registered if required - -## Manual registration -After updating composer, add the ServiceProvider to the providers array in `config/app.php` - -```php -LaravelDoctrine\ORM\DoctrineServiceProvider::class, -``` - -Optionally you can register the EntityManager, Registry and/or Doctrine facade: - -```php -'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class, -'Registry' => LaravelDoctrine\ORM\Facades\Registry::class, -'Doctrine' => LaravelDoctrine\ORM\Facades\Doctrine::class, -``` - -## Config -To publish the config use: - -```php -php artisan vendor:publish --tag="config" --provider="LaravelDoctrine\ORM\DoctrineServiceProvider" -``` - -Available environment variables inside the config are: `APP_DEBUG`, `DOCTRINE_METADATA`, `DB_CONNECTION`, `DOCTRINE_PROXY_AUTOGENERATE`, `DOCTRINE_LOGGER` and `DOCTRINE_CACHE` - -> Important: -> By default, Laravel's application skeleton has its `Model` classes in the `app/Models` folder. With Doctrine, you'll need to -> create a dedicated folder for your `Entities` and point your `config/doctrine.php` `paths` array to it. -> If you don't, Doctrine will scan your whole `app/` folder for files, which will have a huge impact on performance! -> -> ``` -> 'paths' => [ -> base_path('app/Entities'), -> ], -> ``` diff --git a/docs/introduction.md b/docs/introduction.md deleted file mode 100644 index 2659a8ce..00000000 --- a/docs/introduction.md +++ /dev/null @@ -1,27 +0,0 @@ -# Introduction - -#### A drop-in Doctrine ORM 2 implementation for Laravel 6+ - -Doctrine 2 is an object-relational mapper (ORM) for PHP that provides transparent persistence for PHP objects. -It uses the Data Mapper pattern at the heart, aiming for a complete separation of -your domain/business logic from the persistence in a relational database management system. - -The benefit of Doctrine for the programmer is the ability to focus on the object-oriented business -logic and worry about persistence only as a secondary problem. This doesn’t mean persistence is downplayed by Doctrine 2, -however it is our belief that there are considerable benefits for object-oriented programming if persistence and entities are kept separated. - -*Laravel Doctrine offers:* - -* Easy configuration -* Pagination -* Preconfigured metadata, connections and caching -* Extendable: extend or add your own drivers for metadata, connections or cache -* Change metadata, connection or cache settings easy with a resolved hook -* Annotations, yaml, xml, config and static php meta data mappings -* Multiple entity managers and connections -* Laravel naming strategy -* Simple authentication implementation -* Password reminders implementation -* Doctrine console commands -* DoctrineExtensions supported -* Timestamps, Softdeletes and TablePrefix listeners diff --git a/docs/lumen.md b/docs/lumen.md deleted file mode 100644 index 388daf4e..00000000 --- a/docs/lumen.md +++ /dev/null @@ -1,127 +0,0 @@ -# Installation in Lumen 6+ - - Lumen | Laravel Doctrine -:---------|:---------- - 6.* | ~1.5 - 7.* | ~1.6 - 8.* | ~1.7 - -To set up Laravel Doctrine in Lumen, we need some additional steps. - -Install this package with composer: - -``` -composer require "laravel-doctrine/orm:1.7.*" -``` - -After updating composer, open `bootstrap/app.php` and register the Service Provider: - -```php -$app->register(LaravelDoctrine\ORM\DoctrineServiceProvider::class); -``` - -Optionally you can register the EntityManager, Registry and/or Doctrine Facade. Don't forget to uncomment `$app->withFacades();` - -```php -class_alias('LaravelDoctrine\ORM\Facades\EntityManager', 'EntityManager'); -class_alias('LaravelDoctrine\ORM\Facades\Registry', 'Registry'); -class_alias('LaravelDoctrine\ORM\Facades\Doctrine', 'Doctrine'); -``` - -Uncomment `// Dotenv::load(__DIR__.'/../');`, so environment variables can be loaded - -Next, you will need to create the `config/database.php` and `config/cache.php` config files. - -The database config file should look at least like this (assuming you are using MYSQL), but you can copy it from the Laravel source too: - -```php - env('DB_CONNECTION', 'mysql'), - /* - |-------------------------------------------------------------------------- - | Database Connections - |-------------------------------------------------------------------------- - | - | Here are each of the database connections setup for your application. - | Of course, examples of configuring each database platform that is - | supported by Laravel is shown below to make development simple. - | - | - | All database work in Laravel is done through the PHP PDO facilities - | so make sure you have the driver for your particular database of - | choice installed on your machine before you begin development. - | - */ - 'connections' => [ - 'mysql' => [ - 'driver' => 'mysql', - 'host' => env('DB_HOST', 'localhost'), - 'database' => env('DB_DATABASE'), - 'username' => env('DB_USERNAME'), - 'password' => env('DB_PASSWORD'), - 'charset' => 'utf8', - 'collation' => 'utf8_unicode_ci', - 'prefix' => '', - 'strict' => false, - ] - ], -]; -``` - -If you are using apc, file, memcached or redis cache, the following config should be added: - -```php - [ - 'apc' => [ - 'driver' => 'apc', - ], - 'file' => [ - 'driver' => 'file', - 'path' => storage_path('framework/cache'), - ], - 'memcached' => [ - 'driver' => 'memcached', - 'servers' => [ - [ - 'host' => '127.0.0.1', 'port' => 11211, 'weight' => 100, - ], - ], - ], - 'redis' => [ - 'driver' => 'redis', - 'connection' => 'default', - ], - ], - ]; -``` - -### Config - -If you want to overrule the Doctrine config. You will have to create a `config/doctrine.php` file and copy the contents from the package config. - -Available environment variables inside the config are: `APP_DEBUG`, `DOCTRINE_METADATA`, `DB_CONNECTION`, `DOCTRINE_PROXY_AUTOGENERATE`, `DOCTRINE_LOGGER` and `DOCTRINE_CACHE` diff --git a/docs/meta-data-configuration.md b/docs/meta-data-configuration.md deleted file mode 100644 index 8233c990..00000000 --- a/docs/meta-data-configuration.md +++ /dev/null @@ -1,66 +0,0 @@ -# Meta Data - -### Annotations - -This package supports Doctrine annotations meta data and can be enabled inside the config. - -### XML - -This package supports Doctrine xml meta data and can be enabled inside the config. - -### SimplifiedXML - -This package supports simplified Doctrine xml meta data and can be enabled inside the config. - -The format of the `paths` config value in `doctrine.php` config differs sligthly from the default. The path should be passed as key, the namespace as value. - -``` -'paths' => [ - '/path/to/files1' => 'MyProject\Entities', - '/path/to/files2' => 'OtherProject\Entities' -], -``` - -Check the Doctrine documentation for more information: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/xml-mapping.html#simplified-xml-driver - -### YAML - -> **NOTE:** The YAML driver is deprecated and will be removed in Doctrine 3.0. - -This package supports Doctrine yml meta data and can be enabled inside the config. - -### SimplifiedYAML - - > **NOTE:** The YAML driver is deprecated and will be removed in Doctrine 3.0. - -This package supports simplified Doctrine yml meta data and can be enabled inside the config. - -The format of the `paths` config value in `doctrine.php` config differs sligthly from the default. The path should be passed as key, the namespace as value. - -``` -'paths' => [ - '/path/to/files1' => 'MyProject\Entities', - '/path/to/files2' => 'OtherProject\Entities' -], -``` - -Check the Doctrine documentation for more information: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/yaml-mapping.html#simplified-yaml-driver - -### StaticPhp - -This package supports static PHP (`static_php`) meta data and can be enabled inside the config. - -### Config - -This package supports using config meta data and can be enabled inside the config. - -## Extending or Adding Metadata Drivers -Drivers can be replaced or added using `LaravelDoctrine\ORM\Configuration\MetaData\MetaDataManager`. The callback should return an instance of `\Doctrine\Common\Persistence\Mapping\Driver\MappingDriver` - -```php -public function boot(MetaDataManager $metadata) { - $metadata->extend('myDriver', function(Application $app) { - return new FluentDriver(); - }); -} -``` diff --git a/docs/meta-data.md b/docs/meta-data.md deleted file mode 100644 index fcc9189e..00000000 --- a/docs/meta-data.md +++ /dev/null @@ -1,213 +0,0 @@ -# Meta Data - -Because the Entity doesn't extend any smart base class, we will have to tell Doctrine how to map the data from the database into the entity. There're multiple ways of doing this: - -### Annotations - -The annotation driver is set as default meta driver. It searches the entities in the `app` folder, but you can change this to whatever folder (or multiple folders) you like. Annotations means, that you will use docblocks to indicate the column mappings. - -```php -id; - } - - public function getTitle() - { - return $this->title; - } - - public function setTitle($title) - { - $this->title = $title; - } -} -``` - -More about the annotation driver: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/annotations-reference.html#annotations-reference - -### Attributes - -The attributes driver requires php 8 or above. It searches the entities in the `app` folder, but you can change this to whatever folder (or multiple folders) you like. Attributes means, that you will use attributes to indicate the column mappings. - -```php -id; - } - - public function getTitle() - { - return $this->title; - } - - public function setTitle($title) - { - $this->title = $title; - } -} -``` - -More about the attributes driver: https://www.doctrine-project.org/projects/doctrine-orm/en/2.11/reference/attributes-reference.html - -### YAML - -> **NOTE:** The YAML driver is deprecated and will be removed in Doctrine 3.0. - -If you prefer Yaml, you can easily switch the meta driver to `yaml`. It's better to change the meta data paths to something like `config_path('mappings')` instead of adding them all to the `app` folder. - -```yaml -# App.Entities.Article.dcm.yml -App\Entities\Article: - type: entity - table: articles - id: - id: - type: integer - generator: - strategy: AUTO - fields: - title: - type: string -``` - -More about the YAML driver: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/yaml-mapping.html - -### XML - -Another option is to leverage XML mappings. Just like YAML it's better to change the meta data paths to something like `config_path('mappings')`. - -```xml -// App.Article.dcm.xml - - - - - - - - - - - - - - -``` - -More information about XML mappings: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/xml-mapping.html - -### Config files - -This package adds another option, which leverages Laravel's config system. In addition to setting `meta`, this also requires setting `mapping_file`. You could for example create a `config/mappings.php` file to provide mapping information. Here is an example of setting the `meta` and `mapping_file` config properties inside of `config/doctrine.php` to use config file-based metadata: - -```php - [ - 'default' => [ - 'meta' => env('DOCTRINE_METADATA', 'config'), - 'mapping_file' => 'mappings', -``` - -The array structure in `config/mappings.php` is almost identical to the YAML one: - -```php - [ - 'type' => 'entity', - 'table' => 'articles', - 'id' => [ - 'id' => [ - 'type' => 'integer', - 'generator' => [ - 'strategy' => 'auto' - ] - ], - ], - 'fields' => [ - 'title' => [ - 'type' => 'string' - ] - ] - ] -]; -``` - -### Static PHP - -When you change the meta data driver setting to `static_php`, your entities will expect a `loadMetadata` method. - -```php -mapField(array( - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer' - )); - - $metadata->mapField(array( - 'fieldName' => 'title', - 'type' => 'string' - )); - } -} -``` - -More on the StaticPHP driver: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/php-mapping.html diff --git a/docs/metadata.rst b/docs/metadata.rst new file mode 100644 index 00000000..aa3bc0b5 --- /dev/null +++ b/docs/metadata.rst @@ -0,0 +1,166 @@ +======== +Metadata +======== + +Because Doctrine entities dont extend any smart base class, we have to tell +Doctrine how to map the data from the database into the entity. There are +multiple ways of doing this. + + +Attributes +========== + +The attributes driver requires php 8 or above. It searches the entities in +the `app` folder, but you can change this to whatever folder +(or multiple folders) you like. Attributes means, that you will use attributes +to indicate the column mappings. + +.. code-block:: php + + namespace App\Entities; + + use Doctrine\ORM\Mapping\Column; + use Doctrine\ORM\Mapping\Entity; + use Doctrine\ORM\Mapping\GeneratedValue; + use Doctrine\ORM\Mapping\Id; + use Doctrine\ORM\Mapping\Table; + + #[Entity] + #[Table(name: "articles")] + class Article + { + #[Id, Column(type: "integer"), GeneratedValue()] + protected $id; + + #[Column(type: "string")] + protected $title; + + public function getId() + { + return $this->id; + } + + public function getTitle() + { + return $this->title; + } + + public function setTitle($title) + { + $this->title = $title; + } + } + +More about the attributes driver: +https://www.doctrine-project.org/projects/doctrine-orm/en/2.11/reference/attributes-reference.html + + +XML +=== + +Another option is to us XML mappings. It's better to change the + meta data paths to something like ``config_path('doctrine_orm_metadata')``. + +App.Entities.Article.dcm.xml + +.. code-block:: xml + + + + + + + + + + + + +``` + +More information about XML mappings: +https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/xml-mapping.html + +### Config files + +This package adds another option, which leverages Laravel's config system. +In addition to setting `meta`, this also requires setting `mapping_file`. +You could for example create a `config/mappings.php` file to provide mapping +information. Here is an example of setting the `meta` and `mapping_file` +config properties inside of `config/doctrine.php` to use config file-based +metadata. + +.. code-block:: php + + return [ + 'managers' => [ + 'default' => [ + 'meta' => env('DOCTRINE_METADATA', 'config'), + 'mapping_file' => 'mappings', + + +The array structure in `config/mappings.php`: + +.. code-block:: php + + return [ + 'App\Entities\Article' => [ + 'type' => 'entity', + 'table' => 'articles', + 'id' => [ + 'id' => [ + 'type' => 'integer', + 'generator' => [ + 'strategy' => 'auto' + ] + ], + ], + 'fields' => [ + 'title' => [ + 'type' => 'string' + ] + ] + ] + ]; + + +Static PHP +========== + +When you change the meta data driver setting to `static_php`, your entities +will expect a `loadMetadata` method. + +.. code-block:: php + +class Article +{ + protected $id; + protected $title; + + public static function loadMetadata(Doctrine\ORM\Mapping\ClassMetadata $metadata) + { + $metadata->mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer' + )); + + $metadata->mapField(array( + 'fieldName' => 'title', + 'type' => 'string' + )); + } +} +``` + +More on the StaticPHP driver: +https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/php-mapping.html + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/multiple-connections.md b/docs/multiple-connections.md deleted file mode 100644 index 8f2dc744..00000000 --- a/docs/multiple-connections.md +++ /dev/null @@ -1,7 +0,0 @@ -# Multiple Connections - -You can use multiple Doctrine entity managers or connections. This is necessary if you are using different databases or even vendors with entirely different sets of entities. In other words, one entity manager that connects to one database will handle some entities while another entity manager that connects to another database might handle the rest. - -The default manager is configured in `doctrine.managers`. This one will get used when you use the `EntityManager` directly. You can add more managers to this array. - -In your application you can inject `Doctrine\Common\Persistence\ManagerRegistry`. This holds all entity managers. `ManagerRegistry@getManager()` will return the default manager. By passing through the manager name, you will get the connection you want. Alternatively you can use `getManagerForClass($entityName)` to get a manager which is suitable for that entity. diff --git a/docs/multiple-connections.rst b/docs/multiple-connections.rst new file mode 100644 index 00000000..fdeceef6 --- /dev/null +++ b/docs/multiple-connections.rst @@ -0,0 +1,26 @@ +==================== +Multiple Connections +==================== + +You can use multiple Doctrine entity managers or connections. This is necessary +if you are using different databases or even vendors with entirely different +sets of entities. In other words, one entity manager that connects to one +database will handle some entities while another entity manager that connects +to another database might handle the rest. + +The default manager is configured in ``doctrine.managers``. This one will get +used when you use the ``EntityManager`` directly. You can add more managers +to this array. + +In your application, you can inject +``Doctrine\Common\Persistence\ManagerRegistry``. This holds all entity managers. +``ManagerRegistry@getManager()`` will return the default manager. By passing +through the manager name, you will get the connection you want. Alternatively +you can use ``getManagerForClass($entityName)`` to get a manager which is +suitable for that entity. + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst diff --git a/docs/notifications.md b/docs/notifications.md deleted file mode 100644 index 790b3aac..00000000 --- a/docs/notifications.md +++ /dev/null @@ -1,121 +0,0 @@ -# Notifications - -Laravel 5.3 introduced a brand new notification system with support for custom channels like mail, slack, sms, etc. -Laravel Doctrine offers a doctrine channel so notifications can also be stored in your database. - -### Notification entity - -First create a `Notification` entity in your project that extends `LaravelDoctrine\ORM\Notifications\Notification`. -Adding `protected $user` is only needed when you are using `annotations`. All other metadata drivers can use the already existing `$user` property. -It might look like this: - -```php -to($user) - ->success(); - -$n->error(); - -$n->level('info') - ->message('Your notification message') - ->action('Click here', 'http://yoururl.com'); -``` - -It also has getters to retrieve information about the notification: - -```php -$n->getId(); -$n->getUser(); -$n->getLevel(); -$n->getMessage(); -$n->getActionText(); -$n->getActionUrl(); -``` - -### Publishing notifications on the doctrine channel - -It's recommended you read the Laravel docs: https://laravel.com/docs/notifications - -The Doctrine channel is available as: `LaravelDoctrine\ORM\Notifications\DoctrineChannel::class` - -When adding this channel you need to provide a `toEntity` method. This method should return a new instance of your `Notification` class. -You can use the fluent methods like described above. - -```php -to($notifiable) - ->success() - ->message('Some message') - ->action('Bla', 'http://test.nl'); - } -} -``` - -### Notifiable Entity - -Your Notifiable entity should use the `LaravelDoctrine\ORM\Notifications\Notifiable` trait. - -Now you will be able to do `$user->notify(new InvoicePaid);` - -```php -class User -{ - use LaravelDoctrine\ORM\Notifications\Notifiable; -} -``` - -### Custom Entity Manager - -By default the Doctrine Channel will find the first suitable EM to persist the Notification by using the `ManagerRegistry`. - -If you want more control over it, you can specify it inside your notifiable entity (most likely your User entity). Usage of the `LaravelDoctrine\ORM\Notifications\Notifiable` is required. - -```php -public function routeNotificationForDoctrine() -{ - return 'custom'; -} -``` diff --git a/docs/notifications.rst b/docs/notifications.rst new file mode 100644 index 00000000..20d2737d --- /dev/null +++ b/docs/notifications.rst @@ -0,0 +1,132 @@ +============= +Notifications +============= + +Laravel has a notification system with support for custom channels like +mail, slack, sms, etc. Laravel Doctrine offers a doctrine channel so +notifications can also be stored in your database. + +Notification entity +=================== + +Create a `Notification` entity in your project that +extends `LaravelDoctrine\ORM\Notifications\Notification`. +It might look like this: + +.. code-block:: php + + namespace App\Entities; + + use Doctrine\ORM\Mapping AS ORM; + + #[ORM\Entity] + class Notification extends \LaravelDoctrine\ORM\Notifications\Notification + { + #[ORM\ManyToOne(targetEntity: \App\Entities\User::class)] + + protected $user; + } + +``LaravelDoctrine\ORM\Notifications\Notification`` offers a couple of fluent methods: + +.. code-block:: php + + $n->to($user) + ->success(); + + $n->error(); + + $n->level('info') + ->message('Your notification message') + ->action('Click here', 'http://yoururl.com'); + + +It also has getters to retrieve information about the notification: + +.. code-block:: php + + $n->getId(); + $n->getUser(); + $n->getLevel(); + $n->getMessage(); + $n->getActionText(); + $n->getActionUrl(); + + +Publishing notifications on the Doctrine channel +================================================ + +It's recommended you read the Laravel docs on this subject: +https://laravel.com/docs/notifications + +The Doctrine channel is available as: +``LaravelDoctrine\ORM\Notifications\DoctrineChannel::class`` + +When adding this channel you need to provide a ``toEntity`` method. This +method should return a new instance of your ``Notification`` class. +You can use the fluent methods as described above. + +.. code-block:: php + + namespace App\Notifications; + + class InvoicePaid extends \Illuminate\Notifications\Notification + { + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return [\LaravelDoctrine\ORM\Notifications\DoctrineChannel::class]; + } + + /** + * @param $notifiable + * @return $this + */ + public function toEntity($notifiable) + { + return (new \App\Entities\Notification) + ->to($notifiable) + ->success() + ->message('Some message') + ->action('Bla', 'http://test.net'); + } + } + + +Notifiable Entity +================= + +Your Notifiable entity should use the +``LaravelDoctrine\ORM\Notifications\Notifiable`` trait. + +Now you will be able to do ``$user->notify(new InvoicePaid);`` + +.. code-block:: php + + class User + { + use LaravelDoctrine\ORM\Notifications\Notifiable; + } + + +Custom Entity Manager +===================== + +By default the Doctrine Channel will find the first suitable EM to persist +the Notification by using the ``ManagerRegistry``. + +If you want more control over it, you can specify it inside your notifiable +entity (most likely your User entity). Usage of the +``LaravelDoctrine\ORM\Notifications\Notifiable`` is required. + +.. code-block:: php + + public function routeNotificationForDoctrine() + { + return 'custom'; + } diff --git a/docs/pagination.rst b/docs/pagination.rst new file mode 100644 index 00000000..63e97dea --- /dev/null +++ b/docs/pagination.rst @@ -0,0 +1,73 @@ + +========== +Pagination +========== + +If you want to add easy pagination, you can add the +``LaravelDoctrine\ORM\Pagination\PaginatesFromRequest`` trait to your +repositories. It offers two methods: +``paginateAll($perPage = 15, $pageName = 'page')`` +and +``paginate($query, $perPage, $pageName = 'page', $fetchJoinCollection = true)``. + +``paginateAll`` will work out of the box, and will return all +(non-deleted, when soft-deletes are enabled) entities inside Laravel's +``LengthAwarePaginator``. + +If you want to add pagination to your custom queries, you will have to pass +the query object through ``paginate()``. + +.. code-block:: php + + public function paginateAllPublishedScientists($perPage = 15, $pageName = 'page') + { + $builder = $this->createQueryBuilder('o'); + $builder->where('o.status = 1'); + + return $this->paginate($builder->getQuery(), $perPage, $pageName); + } + +The ``PaginatesFromRequest`` trait uses Laravel's ``Request`` object to +fetch current page, just as ``Eloquent`` does by default. If this doesn't +fit your scenario, you can also take advantage of pagination in Doctrine +with the ``LaravelDoctrine\ORM\Pagination\PaginatesFromParams`` trait: + +.. code-block:: php + + namespace App\Repositories; + + use Illuminate\Contracts\Pagination\LengthAwarePaginator; + use LaravelDoctrine\ORM\Pagination\PaginatesFromParams; + + class DoctrineScientistRepository + { + use PaginatesFromParams; + + /** + * @return Scientist[]|LengthAwarePaginator + */ + public function all(int $limit = 8, int $page = 1): LengthAwarePaginator + { + // paginateAll is already public, you may use it directly as well. + return $this->paginateAll($limit, $page); + } + + /** + * @return Scientist[]|LengthAwarePaginator + */ + public function findByName(string $name, int $limit = 8, int $page = 1): LengthAwarePaginator + { + $query = $this->createQueryBuilder('s') + ->where('s.name LIKE :name') + ->orderBy('s.name', 'asc') + ->setParameter('name', "%$name%") + ->getQuery(); + + return $this->paginate($query, $limit, $page); + } + } + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/passwords.md b/docs/passwords.md deleted file mode 100644 index c7c3de68..00000000 --- a/docs/passwords.md +++ /dev/null @@ -1,11 +0,0 @@ -# Password resets - -Most web applications provide a way for users to reset their forgotten passwords. Rather than forcing you to re-implement this on each application, -Laravel provides convenient methods for sending password reminders and performing password resets. - -First off you have to replace Laravel's `PasswordResetServiceProvider` in `config/app.php` by `LaravelDoctrine\ORM\Auth\Passwords\PasswordResetServiceProvider`. This will make sure the querying is handled by Doctrine. - -To get started, verify that your `User` model implements the `Illuminate\Contracts\Auth\CanResetPassword` contract. You can use the `Illuminate\Auth\Passwords\CanResetPassword` -trait, which provides the methods the interface requires. The trait assumes your `email` property is called `email`. - -Read more about the usage in the [Laravel documentation](https://laravel.com/docs/passwords) diff --git a/docs/passwords.rst b/docs/passwords.rst new file mode 100644 index 00000000..9c4e3a92 --- /dev/null +++ b/docs/passwords.rst @@ -0,0 +1,27 @@ +=============== +Password Resets +=============== + +Most web applications provide a way for users to reset their forgotten +passwords. Rather than forcing you to re-implement this on each application, +Laravel provides convenient methods for sending password reminders and +performing password resets. + +First off you have to replace Laravel's +``PasswordResetServiceProvider`` in ``config/app.php`` by +``LaravelDoctrine\ORM\Auth\Passwords\PasswordResetServiceProvider``. +This will make sure the querying is handled by Doctrine. + +To get started, verify that your ``User`` model implements the +``Illuminate\Contracts\Auth\CanResetPassword`` contract. +You can use the ``Illuminate\Auth\Passwords\CanResetPassword`` +trait, which provides the methods the interface requires. The trait assumes +your ``email`` property is called ``email``. + +Read more about the usage in the `Laravel documentation `_. + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst diff --git a/docs/repositories.md b/docs/repositories.md deleted file mode 100644 index 4043ad44..00000000 --- a/docs/repositories.md +++ /dev/null @@ -1,276 +0,0 @@ -# Repositories - -The Repository Design Pattern is one of the most useful and most widely applicable design patterns ever invented. -It works as an abstraction for your persistence layer, giving you a place to write collecting logic, build queries, etc. - -Repositories are usually modeled as collections to abstract away persistence lingo, so it is very common to see methods -like `find($id)`, `findByName("Patrick")`, as if your entities would be in a `Collection` object instead of a database. - -Doctrine comes with a generic `Doctrine\Common\Persistence\ObjectRepository` interface that lets you easily find one, -many or all entities by ID, by an array of filters or by complex `Criteria`, and an implementation of it in -`Doctrine\ORM\EntityRepository`. - -## Getting a repository instance - -The easiest way to get a repository is to let the EntityManager generate one for the Entity you want: - -```php -$repository = EntityManager::getRepository(Scientist::class); -``` - -This will generate an instance of the `Doctrine\ORM\EntityRepository`, a generic implementation ready to be queried for - the class that was given to it. - -## Injecting repositories - -You can inject generic repositories by using Laravel's [contextual binding](https://laravel.com/docs/container#contextual-binding). - -```php -scientists = $scientists; - } -} - -// Then, in one of your ServiceProviders -use App\Entities\Research\Laboratory; -use App\Entities\Research\Scientist; -use Doctrine\Common\Persistence\ObjectRepository; - -class AppServiceProvider -{ - public function register() - { - $this->app - ->when(Laboratory::class) - ->needs(ObjectRepository::class) - ->give(function(){ - return EntityManager::getRepository(Scientist::class); - }); - } -} -``` - -## Extending repositories - -If you want to have more control over these repositories, instead of always calling it on the EntityManager, you can -create your own repository class. When we bind this concrete repository to an interface, it also makes that we can -easily swap the data storage behind them. It also makes testing easier, because we can easily swap the concrete -implementation for a mock. - -Given we have a ScientistRepository: - -```php -findBy(['name' => $name]); - } -} - -// Then, in one of your ServiceProviders -use App\Entities\Research\Scientist; - -class AppServiceProvider -{ - public function register() - { - $this->app->bind(ScientistRepository::class, function($app) { - // This is what Doctrine's EntityRepository needs in its constructor. - return new DoctrineScientistRepository( - $app['em'], - $app['em']->getClassMetaData(Scientist::class) - ); - }); - } -} -``` - -### Reusing repositories through composition - -Sometimes inheritance may not be your preferred way of reusing a library. If you'd rather decouple yourself from its -implementation, if you need a different one or if you are writing a library and don't want to force inheritance on your -consumers, you may choose to reuse Doctrine's generic repository implementation through *composition* instead. - -```php -genericRepository = $genericRepository; - } - - public function find($id) - { - return $this->genericRepository->find($id); - } - - public function findByName($name) - { - return $this->genericRepository->findBy(['name' => $name]); - } -} - -// Then, in one of your ServiceProviders -use App\Entities\Research\Scientist; - -class AppServiceProvider -{ - public function register() - { - $this->app->bind(ScientistRepository::class, function(){ - return new DoctrineScientistRepository( - EntityManager::getRepository(Scientist::class) - ); - }); - } -} -``` - -This method gives you total control over your Repository API. If, for example, you don't want to allow fetching all -Scientist, you simply don't add that method to the interface / implementation, while inheriting the generic Doctrine -repository would force the `findAll()` method on to your `ScientistRepository` API. - -## Using repositories - -Inside your controller (or any object that will be constructed by Laravel), you can now inject your repository interface: - -```php - -class ExampleController extends Controller -{ - private $scientists; - - public function __construct(ScientistRepository $scientists) - { - $this->scientists = $scientists; - } - - public function index() - { - $articles = $this->scientists->findAll(); - } - -} -``` - -More about the EntityRepository: http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.EntityRepository.html - -Learning more about the Repository Pattern: http://shawnmc.cool/the-repository-pattern - -### Pagination - -If you want to add easy pagination, you can add the `LaravelDoctrine\ORM\Pagination\PaginatesFromRequest` trait to your repositories. It offers two methods: `paginateAll($perPage = 15, $pageName = 'page')` and `paginate($query, $perPage, $pageName = 'page', $fetchJoinCollection = true)`. - -`paginateAll` will work out of the box, and will return all (non-deleted, when soft-deletes are enabled) entities inside Laravel's `LengthAwarePaginator`. - -If you want to add pagination to your custom queries, you will have to pass the query object through `paginate()` - -```php -public function paginateAllPublishedScientists($perPage = 15, $pageName = 'page') -{ - $builder = $this->createQueryBuilder('o'); - $builder->where('o.status = 1'); - - return $this->paginate($builder->getQuery(), $perPage, $pageName); -} -``` - -The `PaginatesFromRequest` trait uses Laravel's `Request` object to fetch current page, just as `Eloquent` does by default. If this doesn't fit your scenario, you can also take advantage of pagination in Doctrine with the `LaravelDoctrine\ORM\Pagination\PaginatesFromParams` trait: - -```php -namespace App\Repositories; - -use Illuminate\Contracts\Pagination\LengthAwarePaginator; -use LaravelDoctrine\ORM\Pagination\PaginatesFromParams; - -class DoctrineScientistRepository -{ - use PaginatesFromParams; - - /** - * @return Scientist[]|LengthAwarePaginator - */ - public function all(int $limit = 8, int $page = 1): LengthAwarePaginator - { - // paginateAll is already public, you may use it directly as well. - return $this->paginateAll($limit, $page); - } - - /** - * @return Scientist[]|LengthAwarePaginator - */ - public function findByName(string $name, int $limit = 8, int $page = 1): LengthAwarePaginator - { - $query = $this->createQueryBuilder('s') - ->where('s.name LIKE :name') - ->orderBy('s.name', 'asc') - ->setParameter('name', "%$name%") - ->getQuery(); - - return $this->paginate($query, $limit, $page); - } -} -``` diff --git a/docs/repositories.rst b/docs/repositories.rst new file mode 100644 index 00000000..57daeca0 --- /dev/null +++ b/docs/repositories.rst @@ -0,0 +1,248 @@ +============ +Repositories +============ + +The Repository Design Pattern is one of the most useful and most widely +applicable design patterns ever invented. +It works as an abstraction for your persistence layer, giving you a place to +write collecting logic, build queries, etc. + +Repositories are usually modeled as collections to abstract away persistence +lingo, so it is very common to see methods +like ``find($id)``, ``findByName("Patrick")``, as if your entities would be in +a ``Collection`` object instead of a database. + +Doctrine comes with a generic ``Doctrine\Common\Persistence\ObjectRepository`` +interface that lets you easily find one, +many or all entities by ID, by an array of filters or by complex ``Criteria``, +and an implementation of it in ``Doctrine\ORM\EntityRepository``. + +Getting a repository instance +============================= + +The easiest way to get a repository is to let the EntityManager generate one +for the Entity you want: + +.. code-block:: php + + $repository = EntityManager::getRepository(Scientist::class); + +This will generate an instance of the `Doctrine\ORM\EntityRepository`, a +generic implementation ready to be queried for the class that was given to it. + +Injecting repositories +====================== + +You can inject generic repositories by using Laravel's +`contextual binding `_. + +.. code-block:: php + + namespace App\Entities\Research; + + use Doctrine\Common\Persistence\ObjectRepository; + + class Laboratory + { + /** + * @var ObjectRepository + */ + private $scientists; + + public function __construct(ObjectRepository $scientists) + { + $this->scientists = $scientists; + } + } + + // Then, in one of your ServiceProviders + use App\Entities\Research\Laboratory; + use App\Entities\Research\Scientist; + use Doctrine\Common\Persistence\ObjectRepository; + + class AppServiceProvider + { + public function register() + { + $this->app + ->when(Laboratory::class) + ->needs(ObjectRepository::class) + ->give(function(){ + return EntityManager::getRepository(Scientist::class); + }); + } + } + + +Extending repositories +====================== + +If you want to have more control over these repositories, instead of always +calling it on the EntityManager, you can create your own repository class. +When we bind this concrete repository to an interface, it also makes that +we can easily swap the data storage behind them. It also makes testing easier, +because we can easily swap the concrete implementation for a mock. + +Given we have a ScientistRepository: + +.. code-block:: php + + interface ScientistRepository + { + public function find($id); + public function findByName($name); + } + + +We should be able to make a concrete implementation of it with Doctrine: + +.. code-block:: php + + class DoctrineScientistRepository implements ScientistRepository + { + public function find($id) + { + // implement your find method + } + + public function findByName($name) + { + // implement your find by title method + } + } + + +Of course, now that we've built our own object, we are missing some useful +features from Doctrine's generic repositories. +Let's see two ways of reusing those generic objects inside our code. + + +Reusing repositories through inheritance +======================================== + +Inheritance may be the simplest way of reusing repositories in Doctrine. +We could change our implementation to something like this: + +.. code-block:: php + + use Doctrine\ORM\EntityRepository; + + class DoctrineScientistRepository extends EntityRepository implements ScientistRepository + { + // public function find($id) already implemented in parent class! + + public function findByName($name) + { + return $this->findBy(['name' => $name]); + } + } + + // Then, in one of your ServiceProviders + use App\Entities\Research\Scientist; + + class AppServiceProvider + { + public function register() + { + $this->app->bind(ScientistRepository::class, function($app) { + // This is what Doctrine's EntityRepository needs in its constructor. + return new DoctrineScientistRepository( + $app['em'], + $app['em']->getClassMetaData(Scientist::class) + ); + }); + } + } + + + +Reusing repositories through composition +======================================== + +Sometimes inheritance may not be your preferred way of reusing a library. +If you'd rather decouple yourself from its implementation, if you need a +different one or if you are writing a library and don't want to force +inheritance on your consumers, you may choose to reuse Doctrine's generic +repository implementation through **composition** instead. + +.. code-block:: php + + use Doctrine\Common\Persistence\ObjectRepository; + + class DoctrineScientistRepository implements ScientistRepository + { + private $genericRepository; + + public function __construct(ObjectRepository $genericRepository) + { + $this->genericRepository = $genericRepository; + } + + public function find($id) + { + return $this->genericRepository->find($id); + } + + public function findByName($name) + { + return $this->genericRepository->findBy(['name' => $name]); + } + } + + // Then, in one of your ServiceProviders + use App\Entities\Research\Scientist; + + class AppServiceProvider + { + public function register() + { + $this->app->bind(ScientistRepository::class, function(){ + return new DoctrineScientistRepository( + EntityManager::getRepository(Scientist::class) + ); + }); + } + } + + +This method gives you total control over your Repository API. +If, for example, you don't want to allow fetching all +Scientist, you simply don't add that method to the interface / +implementation, while inheriting the generic Doctrine +repository would force the ``findAll()`` method on to your +``ScientistRepository`` API. + +## Using repositories + +Inside your controller (or any object that will be constructed by Laravel), +you can now inject your repository interface: + +.. code-block:: php + + class ExampleController extends Controller + { + private $scientists; + + public function __construct(ScientistRepository $scientists) + { + $this->scientists = $scientists; + } + + public function index() + { + $articles = $this->scientists->findAll(); + } + + } + +More about the EntityRepository: +https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html + +Learning more about the Repository Pattern: +http://shawnmc.cool/the-repository-pattern + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/testing.md b/docs/testing.md deleted file mode 100644 index 8b983d5a..00000000 --- a/docs/testing.md +++ /dev/null @@ -1,106 +0,0 @@ -# Testing - -- [Entity Factories](#entity-factories) - - -### Entity Factories - -When testing or demonstrating your application you may need to insert some dummy data into the database. To help with -this Laravel Doctrine provides Entity Factories, which are similar to Laravel's Model Factories. These allow you -to define values for each property of your Entities and quickly generate many of them. - -Place your Factory files in `database/factories`. Each file in this directory will be run with the variable `$factory` -being an instance of `LaravelDoctrine\ORM\Testing\Factory`. - -#### Entity Definitions - -To define an Entity simple pass the factory its classname and a callback which details what its properties should be set -to - -```php -$factory->define(App\Entities\User::class, function(Faker\Generator $faker) { - return [ - 'name' => $faker->name, - 'emailAddress' => $faker->email - ]; -}); -``` - -Faker allows you to get Entities with different values each time you generate one. Note that as usual with Doctrine, -we reference class property names, and not database columns! - -The factory allows you to define multiple types of the same Entity using `defineAs` - -```php -$factory->defineAs(App\Entities\User::class, 'admin', function(Faker\Generator $faker) { - return [ - 'name' => $faker->name, - 'emailAddress' => $faker->email, - 'isAdmin' => true - ]; -}); -``` - -#### Using Entity Factories in Seeds and Tests - -After you have defined your Entities you can then use the factory to generate them or insert them directly into the -database. - -A helper named `entity()` is defined to aid you in this or you can use the factory directly. - -To make an instance of an Entity (but not persist it to the database), call - -```php -entity(App\Entities\User::class)->make(); - -// OR - -$factory->of(App\Entities\User::class)->make(); -``` - -If you need an Entity of a specific type (see the 'admin' example above) - -```php -entity(App\Entities\User::class, 'admin')->make(); - -// OR - -$factory->of(App\Entities\User::class, 'admin')->make(); -``` - -If you need multiple Entities - -```php -entity(App\Entities\User::class, 2)->make(); -entity(App\Entities\User::class, 'admin', 2)->make(); - -// OR - -$factory->of(App\Entities\User::class)->times(2)->make(); -``` - -These methods will return an instance of `Illuminate\Support\Collection` containing your Entities. - -If you want to instead persist the Entity before being given an instance of it, replace calls to `->make()` with `->create()`, -e.g: - -```php -entity(App\Entities\User::class)->create(); // The User is now in the database -``` - -#### Passing Extra Attributes to Factories - -Factory definition callbacks may receive an optional second argument of attributes. - -```php -$factory->define(App\Entities\User::class, function(Faker\Generator $faker, array $attributes) { - return [ - 'name' => isset($attributes['name']) ? $attributes['name'] : $faker->name, - 'emailAddress' => $faker->email - ]; -}); - -$user = entity(App\Entities\User::class)->make(['name' => 'Taylor']); - -// $user->getName() = 'Taylor' -``` diff --git a/docs/testing.rst b/docs/testing.rst new file mode 100644 index 00000000..63d8c91d --- /dev/null +++ b/docs/testing.rst @@ -0,0 +1,129 @@ +======= +Testing +======= + +Entity Factories +================ + +When testing or demonstrating your application you may need to insert some +dummy data into the database. To help with +this Laravel Doctrine provides Entity Factories, which are similar to +Laravel's Model Factories. These allow you +to define values for each property of your Entities and quickly generate +many of them. + +Place your Factory files in ``database/factories``. Each file in this +directory will be run with the variable ``$factory`` +being an instance of ``LaravelDoctrine\ORM\Testing\Factory``. + +Entity Definitions +------------------ + +To define an Entity simple pass the factory its classname and a callback +which details what its properties should be set to + +.. code-block:: php + + $factory->define(App\Entities\User::class, function(Faker\Generator $faker) { + return [ + 'name' => $faker->name, + 'emailAddress' => $faker->email + ]; + }); + + +Faker allows you to get Entities with different values each time you +generate one. Note that as usual with Doctrine, +we reference class property (field) names, and not database columns! + +The factory allows you to define multiple types of the same Entity using +``defineAs`` + +.. code-block:: php + + $factory->defineAs(App\Entities\User::class, 'admin', function(Faker\Generator $faker) { + return [ + 'name' => $faker->name, + 'emailAddress' => $faker->email, + 'isAdmin' => true + ]; + }); + + +Using Entity Factories in Seeds and Tests +----------------------------------------- + +After you have defined your Entities you can then use the factory to generate +them or insert them directly into the database. + +A helper named ``entity()`` is defined to aid you in this or you can use +the factory directly. + +To make an instance of an Entity (but not persist it to the database), call + +.. code-block:: php + + entity(App\Entities\User::class)->make(); + + // OR + + $factory->of(App\Entities\User::class)->make(); + + +If you need an Entity of a specific type (see the 'admin' example above) + +.. code-block:: php + + entity(App\Entities\User::class, 'admin')->make(); + + // OR + + $factory->of(App\Entities\User::class, 'admin')->make(); + + +If you need multiple Entities + +.. code-block:: php + + entity(App\Entities\User::class, 2)->make(); + entity(App\Entities\User::class, 'admin', 2)->make(); + + // OR + + $factory->of(App\Entities\User::class)->times(2)->make(); + + +These methods will return an instance of ``Illuminate\Support\Collection`` +containing your Entities. + +If you want to instead persist the Entity before being given an instance of +it, replace calls to ``->make()`` with ``->create()`` + +.. code-block:: php + + entity(App\Entities\User::class)->create(); // The User is now in the database + + +Passing Extra Attributes to Factories +------------------------------------- + +Factory definition callbacks may receive an optional second argument of attributes. + +.. code-block:: php + + $factory->define(App\Entities\User::class, function(Faker\Generator $faker, array $attributes) { + return [ + 'name' => isset($attributes['name']) ? $attributes['name'] : $faker->name, + 'emailAddress' => $faker->email + ]; + }); + + $user = entity(App\Entities\User::class)->make(['name' => 'Taylor']); + + // $user->getName() = 'Taylor' + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md deleted file mode 100644 index 3828975c..00000000 --- a/docs/troubleshooting.md +++ /dev/null @@ -1,43 +0,0 @@ -# Troubleshooting - -Common issues that you may run into when configuring or running Laravel Doctrine. - -## The DatabaseTransactions trait is not working for tests - -You will need to use a new trait instead of the default Laravel trait. Here is an example of an implementation: - -``` -beginTransaction(); - } - - public function tearDownDoctrineDatabaseTransactions(): void - { - EntityManager::getConnection()->rollBack(); - } -} -``` - -If you would like to also use `assertDatabaseHas` in your tests, you need to tell Laravel to use the same connection as Doctrine is using so that it can find rows that have not been committed in a transaction: - -``` -$pdo = app()->make(\Doctrine\ORM\EntityManagerInterface::class)->getConnection() - ->getWrappedConnection(); - -app()->make(\Illuminate\Database\ConnectionInterface::class)->setPdo($pdo); -``` - -## ErrorException: require(.../storage/proxies/__CG__Entity.php): Failed to open stream: No such file or directory - -Proxies need to be generated before they can be used. You can generate the proxies manually using the `php artisan doctrine:generate:proxies` command, or you can enable proxy auto generation in your `doctrine.php` config file. By default, you can set the `DOCTRINE_PROXY_AUTOGENERATE` environment value to true to enable auto generation. - -Note: Proxy auto generation should always be disabled in production for performance reasons. \ No newline at end of file diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst new file mode 100644 index 00000000..4a9a1afe --- /dev/null +++ b/docs/troubleshooting.rst @@ -0,0 +1,63 @@ +=============== +Troubleshooting +=============== + +Common issues that you may run into when configuring or running Laravel Doctrine. + +The DatabaseTransactions trait is not working for tests +======================================================= + +You will need to use a new trait instead of the default Laravel trait. +Here is an example of an implementation: + +.. code-block:: php + + namespace Tests; + + use LaravelDoctrine\ORM\Facades\EntityManager; + + trait DoctrineDatabaseTransactions + { + public function setUpDoctrineDatabaseTransactions(): void + { + EntityManager::getConnection()->beginTransaction(); + } + + public function tearDownDoctrineDatabaseTransactions(): void + { + EntityManager::getConnection()->rollBack(); + } + } + + +If you would like to also use ``assertDatabaseHas`` in your tests, you need +to tell Laravel to use the same connection as Doctrine is using so that it +can find rows that have not been committed in a transaction: + +.. code-block:: php + + $pdo = app()->make(\Doctrine\ORM\EntityManagerInterface::class)->getConnection() + ->getWrappedConnection(); + + app()->make(\Illuminate\Database\ConnectionInterface::class)->setPdo($pdo); + + +Missing Proxy files +=================== + + ErrorException: require(.../storage/proxies/__CG__Entity.php): Failed to open stream: No such file or directory + +Proxies need to be generated before they can be used. You can generate the +proxies manually using the `php artisan doctrine:generate:proxies` command, +or you can enable proxy auto generation in your `doctrine.php` config file. +By default, you can set the `DOCTRINE_PROXY_AUTOGENERATE` environment value +to true to enable auto generation. + +Note: Proxy auto generation should always be disabled in production for +performance reasons. + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file diff --git a/docs/upgrade.md b/docs/upgrade.md deleted file mode 100644 index 21234dcc..00000000 --- a/docs/upgrade.md +++ /dev/null @@ -1,27 +0,0 @@ -# Upgrade Guide - -- [Upgrading from older packages](#upgrade-older) -- [Upgrading from atrauzzi/laravel-doctrine](#upgrade-atrauzzi) -- [Upgrading from mitchellvanw/laravel-doctrine](#upgrade-mitchellvanw) - -## Upgrading from older packages - -An artisan command was added to make the upgrade path a bit lighter. - -`php artisan doctrine:config:convert [author] [--source-file] [--dest-path]` - -To learn more about flags and how it works see [Migrating Config Files From Other Packages](/docs/{{version}}/orm/config-migrator). - -> **NOTE:** This tool is meant to be used AS A STARTING POINT for converting your configuration. In most cases you will still need to inspect and modify the generated configuration to suite your needs. - -## Upgrading from atrauzzi/laravel-doctrine - -If you have used [atrauzzi/laravel-doctrine](https://github.com/atrauzzi/laravel-doctrine) in the past. You can easily update your config file using the following artisan command, a new `doctrine.php` config file will be generated based on your old config file: - -`php artisan doctrine:config:convert atrauzzi [--source-file] [--dest-path]` - -## Upgrading from mitchellvanw/laravel-doctrine - -If you have used [mitchellvanw/laravel-doctrine](https://github.com/mitchellvanw/laravel-doctrine) in the past. You can easily update your config file using the following artisan command, a new `doctrine.php` config file will be generated based on your old config file: - -`php artisan doctrine:config:convert mitchell [--source-file] [--dest-path]` diff --git a/docs/validation.md b/docs/validation.md deleted file mode 100644 index 3a0766c6..00000000 --- a/docs/validation.md +++ /dev/null @@ -1,77 +0,0 @@ -# Validation - -Laravel provides several different approaches to validate your application's incoming data. By default, Laravel's base controller class uses a ValidatesRequests trait which provides a -convenient method to validate incoming HTTP request with a variety of powerful validation rules. - -Both `unique` and `exists` validation rules, require the database to be queried. This packages provides this integration. - -## Unique - -The field under validation must be unique for a given Entity. If the column option is not specified, the field name will be used. - -|Parameter| Description| -|--|--| -| **entity** | fully qualified namespace of your entity | -| **column** | the column that should be unqiue | -| **exceptId** | the id that should be excluded from the query [optional] | -| **idColumn** | alternative id column (defaults to id) [optional] | - -```php -/** - * Store a new blog post. - * - * @param Request $request - * @return Response - */ -public function store(Request $request) -{ - $this->validate($request, [ - 'username' => 'required|unique:App\Entities\User,username', - ]); -} -``` - -### Forcing A Unique Rule To Ignore A Given ID: - -Sometimes, you may wish to ignore a given ID during the unique check. For example, consider an "update profile" screen that includes the user's name, e-mail address, and location. Of course, you will want to verify that the e-mail address is unique. However, if the user only changes the name field and not the e-mail field, you do not want a validation error to be thrown because the user is already the owner of the e-mail address. You only want to throw a validation error if the user provides an e-mail address that is already used by a different user. To tell the unique rule to ignore the user's ID, you may pass the ID as the third parameter: - -``` -'email' => 'unique:users,email_address,'.$user->id -``` - -### Adding Additional Where Clauses: - -You may also specify more conditions that will be added as "where" clauses to the query: - -``` -'email' => 'unique:users,email_address,NULL,id,account_id,1' -``` - -In the rule above, only rows with an account_id of 1 would be included in the unique check. - - -## Exists - -The field under validation must exist on a given database table. - -|Parameter| Description| -|--|--| -| **entity** | fully qualified namespace of your entity | -| **column** | the column that should be unqiue | -| **exceptId** | the id that should be excluded from the query [optional] | -| **idColumn** | alternative id column (defaults to id) [optional] | - -```php -/** - * Store a new blog post. - * - * @param Request $request - * @return Response - */ -public function update($id, Request $request) -{ - $this->validate($request, [ - 'username' => 'required|exists:App\Entities\User,username', - ]); -} -``` diff --git a/docs/validation.rst b/docs/validation.rst new file mode 100644 index 00000000..909c60be --- /dev/null +++ b/docs/validation.rst @@ -0,0 +1,103 @@ +========== +Validation +========== + +Laravel provides several different approaches to validate your +application's incoming data. By default, Laravel's base controller class +uses a ValidatesRequests trait which provides a +convenient method to validate incoming HTTP request with a variety of +powerful validation rules. + +Both ``unique`` and ``exists`` validation rules, require the database to be +queried. This packages provides this integration. + +Unique +====== + +The field under validation must be unique for a given Entity. If the column +option is not specified, the field name will be used. + +* entity - fully qualified namespace of your entity +* column - the column that should be unqiue entity +* exceptId - the id that should be excluded from the query [optional] +* idColumn - alternative id column (defaults to id) [optional] + +.. code-block:: php + + /** + * Store a new blog post. + * + * @param Request $request + * @return Response + */ + public function store(Request $request) + { + $this->validate($request, [ + 'username' => 'required|unique:App\Entities\User,username', + ]); + } + + +Forcing A Unique Rule To Ignore A Given ID +------------------------------------------- + +Sometimes, you may wish to ignore a given ID during the unique check. +For example, consider an "update profile" screen that includes the user's +name, e-mail address, and location. Of course, you will want to verify that +the e-mail address is unique. However, if the user only changes the name +field and not the e-mail field, you do not want a validation error to be +thrown because the user is already the owner of the e-mail address. You only +want to throw a validation error if the user provides an e-mail address that +is already used by a different user. To tell the unique rule to ignore the +user's ID, you may pass the ID as the third parameter. + +.. code-block:: php + + 'email' => 'unique:users,email_address,' . $user->id + + +Adding Additional Where Clauses +------------------------------- + +You may also specify more conditions that will be added as "where" +clauses to the query: + +.. code-block:: php + + 'email' => 'unique:users,email_address,NULL,id,account_id,1' + + +In the rule above, only rows with an account_id of 1 would be included +in the unique check. + + +Exists +====== + +The field under validation must exist on a given database table. + +* entity - fully qualified namespace of your entity +* column - the column that should be unqiue +* exceptId - the id that should be excluded from the query [optional] +* idColumn - alternative id column (defaults to id) [optional] + +.. code-block:: php + + /** + * Store a new blog post. + * + * @param Request $request + * @return Response + */ + public function update($id, Request $request) + { + $this->validate($request, [ + 'username' => 'required|exists:App\Entities\User,username', + ]); + } + + +.. role:: raw-html(raw) + :format: html + +.. include:: footer.rst \ No newline at end of file