eureka / component-orm
PHP orm library.
Requires
- php: 8.1.*||8.2.*||8.3.*
- ext-pdo: *
- eureka/component-console: ^6.1.0
- eureka/component-database: ^3.2
- eureka/component-validation: ^5.2
- psr/cache: ^1.0||^2.0||^3.0
- psr/log: ^1.1||^2.0||^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.49.0
- maglnet/composer-require-checker: ^3.8||^4.7.1
- phpstan/phpstan: ^1.10.59
- phpstan/phpstan-phpunit: ^1.3.16
- phpstan/phpstan-strict-rules: ^1.5.2
- phpunit/phpcov: ^9.0.2
- phpunit/phpunit: ^10.5.10
- symfony/cache: ^5.4.0||^6.4.3
- dev-master
- 6.0.2
- 6.0.1
- 6.0.0
- 5.5.0
- 5.4.0
- 5.3.0
- 5.2.2
- 5.2.1
- 5.2.0
- 5.1.2
- 5.1.1
- 5.1.0
- 5.0.1
- 5.0.0
- 4.3.0
- 4.2.13
- 4.2.12
- 4.2.11
- 4.2.10
- 4.2.9
- 4.2.8
- 4.2.7
- 4.2.6
- 4.2.5
- 4.2.4
- 4.2.3
- 4.2.2
- 4.2.1
- 4.2.0
- 4.0.0
- 3.1.1
- 3.1.0
- 3.0.1
- 3.0.0
- 2.1.1
- 2.1.0
- 2.0.0
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.1
- 1.0.0
- dev-fix_error_with_joined_entity_left
- dev-branch_5.x
This package is auto-updated.
Last update: 2024-10-24 13:30:28 UTC
README
PHP Simple ORM
Composer
To add this ORM to your project, you can use the following command:
~/path/to/your/project/$ composer require "eureka/component-orm"
You can install the component (for testing) with the following command:
make install
Update
You can update the component (for testing) with the following command:
make update
Configuration
If you are using Symfony Dependency Injection & Yaml config, you can configure ORM with something look like this:
Main orm.yaml config
parameters: #~ Comment part orm.comment.author: 'Eureka Orm Generator' orm.comment.copyright: 'My Copyright name' #~ Namespace base config orm.base_namespace.entity: 'Application\Domain' # Base namespace for entities orm.base_namespace.mapper: 'Application\Domain' # Base namespace for mapper (repository interface implementation) orm.base_namespace.repository: 'Application\Domain' # Base namespace for repository interfaces #~ Path base config orm.base_path.entity: '%kernel.directory.root%/src/Domain' # Base path for entities orm.base_path.mapper: '%kernel.directory.root%/src/Domain' # Base path for mapper (repository interface implementation) orm.base_path.repository: '%kernel.directory.root%/src/Domain' # Base namespace for repository interfaces #~ Cache base config orm.cache.enabled: false # Define globally if cache is enable or not orm.cache.prefix: 'website.magiclegacy.' # Cache prefix for application database data #~ Validation - /!\ Require "eureka/component-validation" when validation is enabled orm.validation.enabled: true # false: no validation of value in entities setter orm.validation.auto: true # set true to generate auto validation regarding tables columns definitions #~ Define list of available config. # Alias name will be used in "joins" config part orm.configs: #~ Usage #alias: '%orm.config.{name}%' #~ Core website user: '%orm.config.user%' #~ Core Blog blog_post: '%orm.config.blog_post%' blog_category: '%orm.config.blog_category%' blog_tag: '%orm.config.blog_tag%' blog_post_tag: '%orm.config.blog_post_tag%'
Example table yaml config
# ORM Config file parameters: orm.config.user: #~ Some meta data for phpdoc block comments comment: author: '%orm.comment.author%' copyright: '%orm.comment.copyright%' #~ Namespace for generated files namespace: entity: '%orm.base_namespace.entity%\User\Entity' mapper: '%orm.base_namespace.mapper%\User\Infrastructure\Mapper' repository: '%orm.base_namespace.repository%\User\Repository' #~ Path for generated files path: entity: '%orm.base_path.entity%/User/Entity' mapper: '%orm.base_path.mapper%/User/Infrastructure/Mapper' repository: '%orm.base_path.repository%/User/Repository' #~ Cache configuration for this table cache: enabled: '%orm.cache.enabled%' prefix: '%orm.cache.prefix%user' #~ Table config database: table: 'user' # Name of the table prefix: 'user' # Fields prefix to remove in generated method. (Field user_id will have getId() method with this example) class: classname: 'User' # Name of the class (Do not set namespace here) #~ List of join configuration for "eager or lazy loading" joins: UserPosts: # Suffix name for setter/getter (here: getAllUserPosts() / setAllUserPosts()) eager_loading: false # set to true to allow eager loading config: 'blog_post' # config alias name (see orm.yaml > "orm.configs" part) relation: 'many' # one: Return unique entity | many: return list of all found entities type: 'inner' # inner, left, right keys: user_id: true # key(s) for join. set "true" when column name is same in both table UserAddress: # Suffix name for setter/getter (here: getUserAddress() / setUserAddress()) eager_loading: true # set to true to allow eager loading config: 'user_address' # config alias name (see orm.yaml > "orm.configs" part) relation: 'one' # one: Return unique entity | many: return list of all found entities type: 'inner' # inner, left, right keys: user_id: user_id # You also can mapping name (useful when name differ in both table) #~ Validation configuration # /!\ Require "eureka/component-validation" when validation is enabled validation: enabled: '%orm.validation.enabled%' auto: '%orm.validation.auto%' #~ Optional extended_validation: #~ Define or override validation config for some field if needed #~ Example user_name: #type: string - optional when auto validation is enabled #~ options values are merged with auto validation values. #~ If any value is already defined with auto validation, this value override auto validation value options: min_length: 5
Generator
You must generate all ORM file with following command:
~/path/to/your/project/$ bin/console Orm/Script/Generator
/!\ This command require to have "eureka/component-console" installed & bin/console script in your application (with a kernel-console)
Entity
Entities are "reflection" of the table data. When you retrieve data from database, each entity is representation of one row of corresponding table.
Entity should never been instantiated directly. To get a new entity, you should use the following mapper method:
/** @var \Symfony\Component\DependencyInjection\Container $container */ $postRepository = $container->get('post.mapper'); /** @var \Application\Domain\Blog\Infrastructure\Mapper\PostMapper $postRepository */ $post = $postRepository->newEntity();
Mapper
When you need to retrieve data from database, you have to add a method in mapper to retrieve data, commonly prefixed
by find
or findAll
(according to retrieve one or many entities).
Mapper Example
Retrieve 10 latest blog posts (as entities)
<?php /* * Copyright (c) Romain Cottard * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ declare(strict_types=1); namespace Application\Domain\Blog\Infrastructure\Mapper; use Application\Domain\Blog\Entity\Post; use Application\Domain\Blog\Repository\PostRepositoryInterface; use Eureka\Component\Orm\EntityInterface; use Eureka\Component\Orm\Exception\EntityNotExistsException; use Eureka\Component\Orm\Exception\InvalidQueryException; use Eureka\Component\Orm\Exception\OrmException; use Eureka\Component\Orm\Query\SelectBuilder; /** * Mapper class for table "blog_post" * * @author Eureka Orm Generator */ class PostMapper extends Abstracts\AbstractPostMapper implements PostRepositoryInterface { /** * @param int $number * @return Post[] * @throws InvalidQueryException * @throws OrmException */ public function findLatest(int $number = 10): iterable { //~ Create new query builder (for select) $queryBuilder = new SelectBuilder($this); //~ Add some restriction $queryBuilder->addWhere('blog_post_status', 3); //~ Ordering results $queryBuilder->addOrder('blog_post_id', 'DESC'); //~ Limit number of results $queryBuilder->setLimit($number); //~ select result & return result return $this->select($queryBuilder); } /** * @param int $postId * @return Post[] * @throws OrmException */ public function findPostWithUser(int $postId): iterable { //~ Create new query builder (for select) $queryBuilder = new SelectBuilder($this); //~ Add some restriction $queryBuilder->addWhere('blog_post_id', $postId); //~ Use eager loading to load user attach with Post (join only on user is this example) return $this->selectJoin($queryBuilder, ['user']); } /** * @return Post|EntityInterface * @throws EntityNotExistsException * @throws OrmException */ public function findLast(): Post { //~ Create new query builder (for select) $queryBuilder = new SelectBuilder($this); //~ Add some restriction $queryBuilder->addWhere('blog_post_status', 3); //~ Ordering results $queryBuilder->addOrder('blog_post_id', 'DESC'); //~ When use selectOne(), not found entity will throw an EntityNotExistsException return $this->selectOne($queryBuilder); } }
Repository
To have repository as representation of Driven Design Domain (DDD), ORM generate Repository interfaces that should be used to sign your methods in your application. The real implementations are mapper.
Repository examples
Here, this is an example of repository interface corresponding to the previous implementation above.
<?php /* * Copyright (c) Romain Cottard * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Application\Domain\Blog\Repository; use Application\Domain\Blog\Entity\Post; use Eureka\Component\Orm\EntityInterface; use Eureka\Component\Orm\Exception\EntityNotExistsException; use Eureka\Component\Orm\Exception\InvalidQueryException; use Eureka\Component\Orm\Exception\OrmException; use Eureka\Component\Orm\RepositoryInterface; /** * Post repository interface. * * @author Eureka Orm Generator */ interface PostRepositoryInterface extends RepositoryInterface { /** * @param int $number * @return Post[] * @throws InvalidQueryException * @throws OrmException */ public function findLatest(int $number = 10): iterable; /** * @param int $postId * @return Post[] * @throws OrmException */ public function findPostWithUser(int $postId): iterable; /** * @return Post|EntityInterface * @throws EntityNotExistsException * @throws OrmException */ public function findLast(): Post; }
Testing & CI (Continuous Integration)
You can run tests on your side with following commands:
make tests # run tests with coverage make testdox # run tests without coverage reports but with prettified output
You also can run code style check or code style fixes with following commands:
make phpcs # run checks on check style make phpcbf # run check style auto fix
To perform a static analyze of your code (with phpstan, lvl 9 at default), you can use the following command:
make phpstan
make analyze # Same as phpstan but with CLI output as table
To ensure you code still compatible with current supported version and futures versions of php, you need to run the following commands (both are required for full support):
make php74compatibility # run compatibility check on current minimal version of php we support make php81compatibility # run compatibility check on last version of php we will support in future
And the last "helper" commands, you can run before commit and push is:
make ci
This command clean the previous reports, install component if needed and run tests (with coverage report), check the code style and check the php compatibility check, as it would be done in our CI.
Contributing
See the CONTRIBUTING file.
License
This project is currently under The MIT License (MIT). See LICENCE file for more information.