├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .php_cs ├── .scrutinizer.yml ├── LICENSE ├── README.md ├── codeclimate.yml ├── composer.json ├── lib ├── Blameable │ └── Mapping │ │ └── Driver │ │ └── Fluent.php ├── FluentExtension.php ├── IpTraceable │ └── Mapping │ │ └── Driver │ │ └── Fluent.php ├── Loggable │ └── Mapping │ │ └── Driver │ │ └── Fluent.php ├── Sluggable │ └── Mapping │ │ └── Driver │ │ └── Fluent.php ├── SoftDeleteable │ └── Mapping │ │ └── Driver │ │ └── Fluent.php ├── Sortable │ └── Mapping │ │ └── Driver │ │ └── Fluent.php ├── Timestampable │ └── Mapping │ │ └── Driver │ │ └── Fluent.php ├── Translatable │ └── Mapping │ │ └── Driver │ │ └── Fluent.php ├── Tree │ └── Mapping │ │ └── Driver │ │ └── Fluent.php └── Uploadable │ └── Mapping │ └── Driver │ └── Fluent.php ├── phpunit.xml.dist └── src ├── Buildable.php ├── Builders ├── AbstractBuilder.php ├── Builder.php ├── Delay.php ├── Embedded.php ├── Entity.php ├── EntityListeners.php ├── Field.php ├── GeneratedValue.php ├── Index.php ├── Inheritance │ ├── AbstractInheritance.php │ ├── Inheritance.php │ ├── InheritanceFactory.php │ ├── JoinedTableInheritance.php │ └── SingleTableInheritance.php ├── LifecycleEvents.php ├── Overrides │ ├── AssociationOverride.php │ ├── AttributeOverride.php │ ├── Override.php │ └── OverrideBuilderFactory.php ├── Primary.php ├── Table.php ├── Traits │ ├── Aliases.php │ ├── Constraints.php │ ├── Dates.php │ ├── Fields.php │ ├── Macroable.php │ ├── Queueable.php │ ├── QueuesMacros.php │ └── Relations.php └── UniqueConstraint.php ├── EmbeddableMapping.php ├── EntityMapping.php ├── Extensions ├── ExtensibleClassMetadata.php ├── ExtensibleClassMetadataFactory.php ├── Extension.php ├── Gedmo │ ├── AbstractTrackingExtension.php │ ├── Blameable.php │ ├── ClosureTable.php │ ├── GedmoBuilderHints.php │ ├── GedmoFieldHints.php │ ├── GedmoManyToManyHints.php │ ├── GedmoManyToOneHints.php │ ├── IpTraceable.php │ ├── Locale.php │ ├── Loggable.php │ ├── Mappings │ │ ├── Loggable │ │ │ ├── AbstractLogEntryMapping.php │ │ │ └── LogEntryMapping.php │ │ ├── Translatable │ │ │ ├── AbstractPersonalTranslationMapping.php │ │ │ ├── AbstractTranslationMapping.php │ │ │ └── TranslationMapping.php │ │ └── Tree │ │ │ └── AbstractClosureMapping.php │ ├── MaterializedPath.php │ ├── NestedSet.php │ ├── Sluggable.php │ ├── SoftDeleteable.php │ ├── Sortable.php │ ├── SortableGroup.php │ ├── SortablePosition.php │ ├── Timestampable.php │ ├── Timestamps.php │ ├── Translatable.php │ ├── TranslationClass.php │ ├── Tree.php │ ├── TreeLeft.php │ ├── TreeLevel.php │ ├── TreePath.php │ ├── TreePathHash.php │ ├── TreePathSource.php │ ├── TreeRight.php │ ├── TreeSelfReference.php │ ├── TreeStrategy.php │ ├── Uploadable.php │ ├── UploadableFile.php │ └── Versioned.php └── GedmoExtensions.php ├── Fluent.php ├── FluentDriver.php ├── MappedSuperClassMapping.php ├── Mappers ├── AbstractMapper.php ├── EmbeddableMapper.php ├── EntityMapper.php ├── MappedSuperClassMapper.php ├── Mapper.php └── MapperSet.php ├── Mapping.php └── Relations ├── AbstractRelation.php ├── AssociationCache.php ├── JoinColumn.php ├── ManyToMany.php ├── ManyToOne.php ├── OneToMany.php ├── OneToOne.php ├── Relation.php └── Traits ├── Indexable.php ├── ManyTo.php ├── Orderable.php ├── Ownable.php ├── Owning.php └── Primary.php /.gitattributes: -------------------------------------------------------------------------------- 1 | /tests export-ignore 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | push: 6 | pull_request: 7 | jobs: 8 | tests: 9 | runs-on: 'ubuntu-latest' 10 | name: PHP ${{ matrix.php }} - Doctrine ${{ matrix.doctrine }} 11 | strategy: 12 | matrix: 13 | php: ['8.0', '8.1', '8.2'] 14 | doctrine: ['2.11', '2.12', '2.13', '2.14'] 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Setup PHP 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: ${{ matrix.php }} 21 | ini-values: error_reporting=E_ALL 22 | tools: phpunit, git 23 | 24 | - name: Install Composer dependencies 25 | run: rm -f composer.lock 26 | 27 | - name: Install doctrine/orm ${{ matrix.doctrine }} 28 | run: composer require --no-progress --no-scripts --no-plugins doctrine/orm "~${{ matrix.doctrine }}.0" -v 29 | 30 | - name: Update dependencies 31 | run: composer update --no-interaction 32 | 33 | - name: PHPUnit 34 | run: vendor/bin/phpunit 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .php_cs.cache 5 | .phpunit.result.cache 6 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | exclude('vendor') 5 | ->exclude('tests') 6 | ->in(__DIR__); 7 | 8 | return Symfony\CS\Config\Config::create() 9 | ->setUsingCache(true) 10 | ->level(Symfony\CS\FixerInterface::PSR2_LEVEL) 11 | ->fixers(array( 12 | 'psr4', 13 | 'encoding', 14 | 'short_tag', 15 | 'blankline_after_open_tag', 16 | 'namespace_no_leading_whitespace', 17 | 'no_blank_lines_after_class_opening', 18 | 'single_array_no_trailing_comma', 19 | 'no_empty_lines_after_phpdocs', 20 | 'concat_with_spaces', 21 | 'eof_ending', 22 | 'ordered_use', 23 | 'extra_empty_lines', 24 | 'single_line_after_imports', 25 | 'trailing_spaces', 26 | 'remove_lines_between_uses', 27 | 'return', 28 | 'indentation', 29 | 'linefeed', 30 | 'braces', 31 | 'visibility', 32 | 'unused_use', 33 | 'whitespacy_lines', 34 | 'php_closing_tag', 35 | 'phpdoc_order', 36 | 'phpdoc_params', 37 | 'phpdoc_trim', 38 | 'phpdoc_scalar', 39 | 'short_array_syntax', 40 | 'align_double_arrow', 41 | 'align_equals', 42 | 'lowercase_constants', 43 | 'lowercase_keywords', 44 | 'multiple_use', 45 | 'line_after_namespace', 46 | ))->finder($finder); 47 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | external_code_coverage: 3 | timeout: 600 4 | runs: 4 5 | build: 6 | environment: 7 | variables: 8 | XDEBUG_MODE: 'coverage' 9 | tests: 10 | override: 11 | - 12 | command: 'vendor/bin/phpunit --coverage-clover=phpunit-coverage.xml' 13 | coverage: 14 | file: 'phpunit-coverage.xml' 15 | format: 'php-clover' 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Laravel Doctrine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fluent Mapping Driver 2 | 3 | 4 | 5 | [![GitHub release](https://img.shields.io/github/release/laravel-doctrine/fluent.svg?style=flat-square)](https://packagist.org/packages/laravel-doctrine/fluent) 6 | [![Packagist](https://img.shields.io/packagist/dt/laravel-doctrine/fluent.svg?style=flat-square)](https://packagist.org/packages/laravel-doctrine/fluent) 7 | [![License](https://img.shields.io/packagist/l/laravel-doctrine/fluent.svg?style=flat-square)](https://packagist.org/packages/laravel-doctrine/fluent) 8 | [![Github actions](https://github.com/laravel-doctrine/fluent/workflows/CI/badge.svg?branch=1.x)](https://github.com/laravel-doctrine/fluent/actions?query=workflow%3ACI+branch%3A1.x) 9 | 10 | *A fluent mapping driver for Doctrine2* 11 | 12 | ``` 13 | composer require laravel-doctrine/fluent 14 | ``` 15 | 16 | This mapping driver allows you to manage your mappings in an Object Oriented approach, separating your entities 17 | from your mapping configuration without the need for configuration files like XML or YAML. 18 | This is done by implementing the `LaravelDoctrine\Fluent\Mapping` interface, or extending the abstract classes 19 | provided with this package for an easier use: 20 | `LaravelDoctrine\Fluent\EntityMapping`, `LaravelDoctrine\Fluent\EmbeddableMapping` or `MappedSuperClassMapping`. 21 | 22 | This package provides a fluent Builder over Doctrine's `ClassMetadataBuilder`, aimed at easing usage of 23 | Doctrine's mapping concepts in Laravel. The builder adds syntax sugar and implements the same grammar that you 24 | might use in Laravel migrations. 25 | 26 | ```php 27 | class ScientistMapping extends EntityMapping 28 | { 29 | /** 30 | * Returns the fully qualified name of the class that this mapper maps. 31 | * 32 | * @return string 33 | */ 34 | public function mapFor() 35 | { 36 | return Scientist::class; 37 | } 38 | 39 | /** 40 | * Load the object's metadata through the Metadata Builder object. 41 | * 42 | * @param Fluent $builder 43 | */ 44 | public function map(Fluent $builder) 45 | { 46 | $builder->increments('id'); 47 | $builder->embed(Name::class, 'name'); 48 | 49 | $builder->hasMany(Theory::class, 'theories')->ownedBy('scientist'); 50 | } 51 | } 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /codeclimate.yml: -------------------------------------------------------------------------------- 1 | languages: 2 | PHP: true 3 | exclude_paths: 4 | - "tests/*" 5 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel-doctrine/fluent", 3 | "description": "A fluent PHP mapping driver for Doctrine2.", 4 | "license": "MIT", 5 | "keywords": [ 6 | "doctrine", 7 | "laravel", 8 | "orm", 9 | "data mapper", 10 | "database", 11 | "mappings", 12 | "mapping driver", 13 | "fluent" 14 | ], 15 | "authors": [ 16 | { 17 | "name": "Guido Contreras Woda", 18 | "email": "guiwoda@gmail.com" 19 | }, 20 | { 21 | "name": "Patrick Brouwers", 22 | "email": "patrick@maatwebsite.nl" 23 | } 24 | ], 25 | "require": { 26 | "php": "^7.2|^8.0", 27 | "doctrine/dbal": "^2.10|^3.3", 28 | "doctrine/orm": "^2.6", 29 | "doctrine/inflector": "^1.4|^2.0", 30 | "doctrine/persistence": "^1.3.5|^2.0|^3.0" 31 | }, 32 | "require-dev": { 33 | "phpunit/phpunit": "~8.0|~9.0", 34 | "mockery/mockery": "~1.0", 35 | "beberlei/doctrineextensions": "~1.0", 36 | "zf1/zend-date": "~1.12", 37 | "nesbot/carbon": "*", 38 | "gedmo/doctrine-extensions": "^2.4|^3" 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "LaravelDoctrine\\Fluent\\": "src/", 43 | "Gedmo\\": "lib/" 44 | } 45 | }, 46 | "autoload-dev": { 47 | "psr-4": { 48 | "Tests\\": "tests/" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Blameable/Mapping/Driver/Fluent.php: -------------------------------------------------------------------------------- 1 | getExtension( 39 | $this->getExtensionName() 40 | )); 41 | } 42 | 43 | /** 44 | * Make sure the original driver is Fluent. 45 | * 46 | * @param MappingDriver $driver 47 | * 48 | * @return void 49 | */ 50 | public function setOriginalDriver($driver) 51 | { 52 | $this->originalDriver = $this->extractFluentDriver($driver); 53 | } 54 | 55 | /** 56 | * @param MappingDriver $driver 57 | * 58 | * @return FluentDriver 59 | */ 60 | private function extractFluentDriver(MappingDriver $driver) 61 | { 62 | if ($driver instanceof FluentDriver) { 63 | return $driver; 64 | } 65 | 66 | if ($driver instanceof MappingDriverChain) { 67 | $default = $driver->getDefaultDriver(); 68 | if ($default instanceof FluentDriver) { 69 | return $default; 70 | } 71 | 72 | foreach ($driver->getDrivers() as $namespace => $driver) { 73 | if ($driver instanceof FluentDriver) { 74 | return $driver; 75 | } 76 | } 77 | } 78 | 79 | throw new \UnexpectedValueException('Fluent driver not found in the driver chain.'); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/IpTraceable/Mapping/Driver/Fluent.php: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | 20 | ./src 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Buildable.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 29 | $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy(); 30 | } 31 | 32 | /** 33 | * @return ClassMetadataBuilder 34 | */ 35 | public function getBuilder() 36 | { 37 | return $this->builder; 38 | } 39 | 40 | /** 41 | * @return ClassMetadata 42 | */ 43 | public function getClassMetadata() 44 | { 45 | return $this->builder->getClassMetadata(); 46 | } 47 | 48 | /** 49 | * @return NamingStrategy 50 | */ 51 | public function getNamingStrategy() 52 | { 53 | return $this->namingStrategy; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Builders/Builder.php: -------------------------------------------------------------------------------- 1 | disallowInEmbeddedClasses(); 32 | 33 | $table = new Table($this->builder, $name); 34 | 35 | $this->callbackAndQueue($table, $callback); 36 | 37 | return $table; 38 | } 39 | 40 | /** 41 | * {@inheritdoc} 42 | */ 43 | public function entity(callable $callback = null) 44 | { 45 | $this->disallowInEmbeddedClasses(); 46 | 47 | $entity = new Entity($this->builder, $this->namingStrategy); 48 | 49 | $this->callIfCallable($callback, $entity); 50 | 51 | return $entity; 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | */ 57 | public function inheritance($type, callable $callback = null) 58 | { 59 | $inheritance = Inheritance\InheritanceFactory::create($type, $this->builder); 60 | 61 | $this->callIfCallable($callback, $inheritance); 62 | 63 | return $inheritance; 64 | } 65 | 66 | /** 67 | * {@inheritdoc} 68 | */ 69 | public function singleTableInheritance(callable $callback = null) 70 | { 71 | return $this->inheritance(Inheritance\Inheritance::SINGLE, $callback); 72 | } 73 | 74 | /** 75 | * {@inheritdoc} 76 | */ 77 | public function joinedTableInheritance(callable $callback = null) 78 | { 79 | return $this->inheritance(Inheritance\Inheritance::JOINED, $callback); 80 | } 81 | 82 | /** 83 | * {@inheritdoc} 84 | */ 85 | public function embed($embeddable, $field = null, callable $callback = null) 86 | { 87 | $embedded = new Embedded( 88 | $this->builder, 89 | $this->namingStrategy, 90 | $this->guessSingularField($embeddable, $field), 91 | $embeddable 92 | ); 93 | 94 | $this->callbackAndQueue($embedded, $callback); 95 | 96 | return $embedded; 97 | } 98 | 99 | /** 100 | * {@inheritdoc} 101 | */ 102 | public function override($name, callable $callback) 103 | { 104 | $override = new Overrides\Override( 105 | $this->getBuilder(), 106 | $this->getNamingStrategy(), 107 | $name, 108 | $callback 109 | ); 110 | 111 | $this->queue($override); 112 | 113 | return $override; 114 | } 115 | 116 | /** 117 | * {@inheritdoc} 118 | */ 119 | public function events(callable $callback = null) 120 | { 121 | $events = new LifecycleEvents($this->builder); 122 | 123 | $this->callbackAndQueue($events, $callback); 124 | 125 | return $events; 126 | } 127 | 128 | /** 129 | * {@inheritdoc} 130 | */ 131 | public function listen(callable $callback = null) 132 | { 133 | $events = new EntityListeners($this->builder); 134 | 135 | $this->callbackAndQueue($events, $callback); 136 | 137 | return $events; 138 | } 139 | 140 | /** 141 | * {@inheritdoc} 142 | */ 143 | public function isEmbeddedClass() 144 | { 145 | return $this->builder->getClassMetadata()->isEmbeddedClass; 146 | } 147 | 148 | /** 149 | * @param string $name 150 | * @param callable|null $callback 151 | * 152 | * @return Field 153 | */ 154 | protected function setArray($name, callable $callback = null) 155 | { 156 | return $this->field(Types::ARRAY, $name, $callback); 157 | } 158 | 159 | /** 160 | * @param string $method 161 | * @param array $params 162 | * 163 | * @return mixed 164 | */ 165 | public function __call($method, $params) 166 | { 167 | // Workaround for reserved keywords 168 | if ($method === 'array') { 169 | return call_user_func_array([$this, 'setArray'], $params); 170 | } 171 | 172 | if ($this->hasMacro($method)) { 173 | return $this->queueMacro($method, $params); 174 | } 175 | 176 | throw new InvalidArgumentException('Fluent builder method ['.$method.'] does not exist'); 177 | } 178 | 179 | /** 180 | * {@inheritdoc} 181 | */ 182 | protected function disallowInEmbeddedClasses($message = '') 183 | { 184 | if ($this->isEmbeddedClass()) { 185 | throw new LogicException($message); 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Builders/Delay.php: -------------------------------------------------------------------------------- 1 | embeddable = $embeddable; 37 | $this->relation = $relation; 38 | } 39 | 40 | /** 41 | * @param string|null $prefix 42 | * 43 | * @return Embedded 44 | */ 45 | public function prefix($prefix) 46 | { 47 | $this->columnPrefix = $prefix; 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * @return Embedded 54 | */ 55 | public function noPrefix() 56 | { 57 | $this->columnPrefix = false; 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * Execute the build process. 64 | */ 65 | public function build() 66 | { 67 | $this->builder->addEmbedded( 68 | $this->relation, 69 | $this->embeddable, 70 | $this->columnPrefix 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Builders/Entity.php: -------------------------------------------------------------------------------- 1 | builder->setCustomRepositoryClass($class); 24 | 25 | return $this; 26 | } 27 | 28 | /** 29 | * @return Entity 30 | */ 31 | public function readOnly() 32 | { 33 | $this->builder->setReadOnly(); 34 | 35 | return $this; 36 | } 37 | 38 | /** 39 | * Enables second-level cache on this entity. 40 | * If you want to enable second-level cache, 41 | * you must enable it on the EntityManager configuration. 42 | * Depending on the cache mode selected, you may also need to configure 43 | * lock modes. 44 | * 45 | * @param int $usage Cache mode. use ClassMetadataInfo::CACHE_USAGE_* constants. 46 | * Defaults to READ_ONLY mode. 47 | * @param string|null $region The cache region to be used. Doctrine will use a default region 48 | * for each entity, if none is provided. 49 | * 50 | * @return Entity 51 | * 52 | * @see http://doctrine-orm.readthedocs.org/en/latest/reference/second-level-cache.html 53 | */ 54 | public function cacheable($usage = ClassMetadataInfo::CACHE_USAGE_READ_ONLY, $region = null) 55 | { 56 | $meta = $this->builder->getClassMetadata(); 57 | $meta->enableCache(compact('usage', $region === null ? [] : 'region')); 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * @param string $method 64 | * @param array $params 65 | * 66 | * @return mixed 67 | */ 68 | public function __call($method, $params) 69 | { 70 | if ($this->hasMacro($method)) { 71 | return $this->queueMacro($method, $params); 72 | } 73 | 74 | throw new \InvalidArgumentException('Fluent builder method ['.$method.'] does not exist'); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Builders/EntityListeners.php: -------------------------------------------------------------------------------- 1 | [], 37 | Events::postRemove => [], 38 | Events::prePersist => [], 39 | Events::postPersist => [], 40 | Events::preUpdate => [], 41 | Events::postUpdate => [], 42 | Events::postLoad => [], 43 | Events::loadClassMetadata => [], 44 | Events::onClassMetadataNotFound => [], 45 | Events::preFlush => [], 46 | Events::onFlush => [], 47 | Events::postFlush => [], 48 | Events::onClear => [], 49 | ]; 50 | 51 | /** 52 | * LifecycleEvents constructor. 53 | * 54 | * @param ClassMetadataBuilder $builder 55 | */ 56 | public function __construct(ClassMetadataBuilder $builder) 57 | { 58 | $this->builder = $builder; 59 | } 60 | 61 | /** 62 | * Magically call all methods that match an event name. 63 | * 64 | * @param string $event 65 | * @param array $args 66 | * 67 | * @throws InvalidArgumentException 68 | * 69 | * @return LifecycleEvents 70 | */ 71 | public function __call($event, $args) 72 | { 73 | if (array_key_exists($event, $this->events)) { 74 | array_unshift($args, $event); 75 | 76 | return call_user_func_array([$this, 'add'], $args); 77 | } 78 | 79 | throw new InvalidArgumentException('Fluent builder method ['.$event.'] does not exist'); 80 | } 81 | 82 | /** 83 | * @param string $event 84 | * @param string $class 85 | * @param string|null $method 86 | * 87 | * @return EntityListeners 88 | */ 89 | private function add($event, $class, $method = null) 90 | { 91 | $this->events[$event][] = [ 92 | 'class' => $class, 93 | 'method' => $method ?: $event, 94 | ]; 95 | 96 | return $this; 97 | } 98 | 99 | /** 100 | * Execute the build process. 101 | */ 102 | public function build() 103 | { 104 | foreach ($this->events as $event => $listeners) { 105 | foreach ($listeners as $listener) { 106 | $this->builder->getClassMetadata()->addEntityListener($event, $listener['class'], $listener['method']); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Builders/GeneratedValue.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 53 | $this->classMetadata = $classMetadata; 54 | } 55 | 56 | /** 57 | * Tells Doctrine to pick the strategy that is preferred by the used database platform. The preferred strategies 58 | * are IDENTITY for MySQL, SQLite, MsSQL and SQL Anywhere and SEQUENCE for Oracle and PostgreSQL. This strategy 59 | * provides full portability. 60 | * 61 | * @param string|null $name 62 | * @param string|null $initial 63 | * @param string|null $size 64 | * 65 | * @return $this 66 | */ 67 | public function auto($name = null, $initial = null, $size = null) 68 | { 69 | $this->strategy = 'AUTO'; 70 | $this->customize($name, $initial, $size); 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Tells Doctrine to use a database sequence for ID generation. This strategy does currently not provide full 77 | * portability. Sequences are supported by Oracle, PostgreSql and SQL Anywhere. 78 | * 79 | * @param string|null $name 80 | * @param string|null $initial 81 | * @param string|null $size 82 | * 83 | * @return $this 84 | */ 85 | public function sequence($name = null, $initial = null, $size = null) 86 | { 87 | $this->strategy = 'SEQUENCE'; 88 | $this->customize($name, $initial, $size); 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * Tells Doctrine to use special identity columns in the database that generate a value on insertion of a row. 95 | * This strategy does currently not provide full portability and is supported by the following platforms: 96 | * MySQL/SQLite/SQL Anywhere (AUTO_INCREMENT), MSSQL (IDENTITY) and PostgreSQL (SERIAL). 97 | * 98 | * @return $this 99 | */ 100 | public function identity() 101 | { 102 | $this->strategy = 'IDENTITY'; 103 | 104 | return $this; 105 | } 106 | 107 | /** 108 | * Tells Doctrine to use the built-in Universally Unique Identifier generator. 109 | * This strategy provides full portability. 110 | * 111 | * @return $this 112 | */ 113 | public function uuid() 114 | { 115 | $this->strategy = 'UUID'; 116 | 117 | return $this; 118 | } 119 | 120 | /** 121 | * Tells Doctrine that the identifiers are assigned (and thus generated) by your code. The assignment must take 122 | * place before a new entity is passed to EntityManager#persist. 123 | * NONE is the same as leaving off the @GeneratedValue entirely. 124 | * 125 | * @return $this 126 | */ 127 | public function none() 128 | { 129 | $this->strategy = 'NONE'; 130 | 131 | return $this; 132 | } 133 | 134 | /** 135 | * Tells Doctrine to use a custom Generator class to generate identifiers. 136 | * The given class must extend \Doctrine\ORM\Id\AbstractIdGenerator. 137 | * 138 | * @param string $generatorClass 139 | * 140 | * @return $this 141 | */ 142 | public function custom($generatorClass) 143 | { 144 | $this->strategy = 'CUSTOM'; 145 | $this->generator = $generatorClass; 146 | 147 | return $this; 148 | } 149 | 150 | /** 151 | * @param string|null $name 152 | * @param string|null $initial 153 | * @param string|null $size 154 | */ 155 | private function customize($name, $initial, $size) 156 | { 157 | $this->name = $name ?: $this->name; 158 | $this->initial = $initial ?: $this->initial; 159 | $this->size = $size ?: $this->size; 160 | } 161 | 162 | /** 163 | * Execute the build process. 164 | */ 165 | public function build() 166 | { 167 | $this->builder->generatedValue($this->strategy); 168 | 169 | if ($this->name) { 170 | $this->builder->setSequenceGenerator($this->name, $this->size, $this->initial); 171 | } 172 | 173 | if ($this->generator) { 174 | $this->classMetadata->setCustomGeneratorDefinition(['class' => $this->generator]); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/Builders/Index.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 44 | $this->columns = $columns; 45 | } 46 | 47 | /** 48 | * Execute the build process. 49 | */ 50 | public function build() 51 | { 52 | $this->builder->addIndex( 53 | $this->getColumns(), 54 | $this->getName() 55 | ); 56 | } 57 | 58 | /** 59 | * @param string $name 60 | * 61 | * @return $this 62 | */ 63 | public function name($name) 64 | { 65 | $this->name = $name; 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * @return string[] 72 | */ 73 | public function getColumns() 74 | { 75 | return $this->columns; 76 | } 77 | 78 | /** 79 | * @return string 80 | */ 81 | public function getName() 82 | { 83 | return $this->name ?: $this->generateIndexName(); 84 | } 85 | 86 | /** 87 | * @return string 88 | */ 89 | protected function generateIndexName() 90 | { 91 | $table = $this->builder->getClassMetadata()->getTableName(); 92 | 93 | return $table.$this->separator.implode($this->separator, $this->getColumns()).$this->separator.$this->suffix; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Builders/Inheritance/AbstractInheritance.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 24 | $this->setType(); 25 | } 26 | 27 | /** 28 | * Set inheritance type. 29 | */ 30 | abstract protected function setType(); 31 | 32 | /** 33 | * Add the discriminator column. 34 | * 35 | * @param string $column 36 | * @param string $type 37 | * @param int $length 38 | * 39 | * @return Inheritance 40 | */ 41 | public function column($column, $type = 'string', $length = 255) 42 | { 43 | $this->builder->setDiscriminatorColumn($column, $type, $length); 44 | 45 | return $this; 46 | } 47 | 48 | /** 49 | * @param string $name 50 | * @param string|null $class 51 | * 52 | * @return Inheritance 53 | */ 54 | public function map($name, $class = null) 55 | { 56 | if (is_array($name)) { 57 | foreach ($name as $name => $class) { 58 | $this->map($name, $class); 59 | } 60 | 61 | return $this; 62 | } 63 | 64 | $this->builder->addDiscriminatorMapClass($name, $class); 65 | 66 | return $this; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Builders/Inheritance/Inheritance.php: -------------------------------------------------------------------------------- 1 | JoinedTableInheritance::class, 15 | 'SINGLE_TABLE' => SingleTableInheritance::class, 16 | Inheritance::SINGLE => SingleTableInheritance::class, 17 | Inheritance::JOINED => JoinedTableInheritance::class, 18 | ]; 19 | 20 | /** 21 | * @param string $type 22 | * @param ClassMetadataBuilder $builder 23 | * 24 | * @return Inheritance 25 | */ 26 | public static function create($type, ClassMetadataBuilder $builder) 27 | { 28 | if (isset(static::$map[$type])) { 29 | return new static::$map[$type]($builder); 30 | } 31 | 32 | throw new InvalidArgumentException("Inheritance type [{$type}] does not exist. SINGLE_TABLE and JOINED are support"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Builders/Inheritance/JoinedTableInheritance.php: -------------------------------------------------------------------------------- 1 | builder->setJoinedTableInheritance(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Builders/Inheritance/SingleTableInheritance.php: -------------------------------------------------------------------------------- 1 | builder->setSingleTableInheritance(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Builders/LifecycleEvents.php: -------------------------------------------------------------------------------- 1 | [], 32 | Events::postRemove => [], 33 | Events::prePersist => [], 34 | Events::postPersist => [], 35 | Events::preUpdate => [], 36 | Events::postUpdate => [], 37 | Events::postLoad => [], 38 | Events::preFlush => [], 39 | ]; 40 | 41 | /** 42 | * LifecycleEvents constructor. 43 | * 44 | * @param ClassMetadataBuilder $builder 45 | */ 46 | public function __construct(ClassMetadataBuilder $builder) 47 | { 48 | $this->builder = $builder; 49 | } 50 | 51 | /** 52 | * Magically call all methods that match an event name. 53 | * 54 | * @param string $method 55 | * @param array $args 56 | * 57 | * @throws InvalidArgumentException 58 | * 59 | * @return LifecycleEvents 60 | */ 61 | public function __call($method, $args) 62 | { 63 | if (array_key_exists($method, $this->events)) { 64 | array_unshift($args, $method); 65 | 66 | return call_user_func_array([$this, 'add'], $args); 67 | } 68 | 69 | throw new InvalidArgumentException('Fluent builder method ['.$method.'] does not exist'); 70 | } 71 | 72 | /** 73 | * @param string $event 74 | * @param string $method 75 | * 76 | * @return LifecycleEvents 77 | */ 78 | private function add($event, $method) 79 | { 80 | $this->events[$event][] = $method; 81 | 82 | return $this; 83 | } 84 | 85 | /** 86 | * Execute the build process. 87 | */ 88 | public function build() 89 | { 90 | foreach ($this->events as $event => $methods) { 91 | foreach ($methods as $method) { 92 | $this->builder->addLifecycleEvent($method, $event); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Builders/Overrides/AssociationOverride.php: -------------------------------------------------------------------------------- 1 | ManyToOne::class, 41 | ClassMetadataInfo::MANY_TO_MANY => ManyToMany::class, 42 | ]; 43 | 44 | /** 45 | * @param ClassMetadataBuilder $builder 46 | * @param NamingStrategy $namingStrategy 47 | * @param string $name 48 | * @param callable $callback 49 | */ 50 | public function __construct( 51 | ClassMetadataBuilder $builder, 52 | NamingStrategy $namingStrategy, 53 | $name, 54 | callable $callback 55 | ) { 56 | $this->builder = $builder; 57 | $this->callback = $callback; 58 | $this->name = $name; 59 | $this->namingStrategy = $namingStrategy; 60 | } 61 | 62 | /** 63 | * Execute the build process. 64 | */ 65 | public function build() 66 | { 67 | $callback = $this->callback; 68 | 69 | // We will create a new class metadata builder instance, 70 | // so we can use it to easily generated a new mapping 71 | // array, without re-declaring the existing association 72 | $builder = $this->newClassMetadataBuilder(); 73 | $source = $this->convertToMappingArray($this->builder); 74 | 75 | if (!isset($this->relations[$source['type']])) { 76 | throw new InvalidArgumentException('Only ManyToMany and ManyToOne relations can be overridden'); 77 | } 78 | 79 | // Create a new association builder, based on the given type 80 | $associationBuilder = $this->getAssociationBuilder($builder, $source); 81 | 82 | // Give the original join table name, so we won't 83 | // accidentally remove custom join table names 84 | if ($this->hasJoinTable($source)) { 85 | $associationBuilder->setJoinTable($source['joinTable']['name']); 86 | } 87 | 88 | $association = $callback($associationBuilder); 89 | 90 | // When the user forget to return, use the $associationBuilder instance 91 | // which contains the same information 92 | $association = $association ?: $associationBuilder; 93 | 94 | if (!$association instanceof Relation) { 95 | throw new InvalidArgumentException('The callback should return an instance of '.Relation::class); 96 | } 97 | 98 | $association->build(); 99 | 100 | $target = $this->convertToMappingArray($builder); 101 | 102 | $overrideMapping = []; 103 | 104 | // ManyToMany mappings 105 | if ($this->hasJoinTable($target)) { 106 | $overrideMapping['joinTable'] = $this->mapJoinTable( 107 | $target['joinTable'], 108 | $source['joinTable'] 109 | ); 110 | } 111 | 112 | // ManyToOne mappings 113 | if ($this->hasJoinColumns($target)) { 114 | $overrideMapping['joinColumns'] = $this->mapJoinColumns( 115 | $target['joinColumns'], 116 | $source['joinColumns'] 117 | ); 118 | } 119 | 120 | $this->builder->getClassMetadata()->setAssociationOverride( 121 | $this->name, 122 | $overrideMapping 123 | ); 124 | } 125 | 126 | /** 127 | * @param ClassMetadataBuilder $builder 128 | * 129 | * @throws \Doctrine\ORM\Mapping\MappingException 130 | * 131 | * @return array 132 | */ 133 | protected function convertToMappingArray(ClassMetadataBuilder $builder) 134 | { 135 | $metadata = $builder->getClassMetadata(); 136 | 137 | return $metadata->getAssociationMapping($this->name); 138 | } 139 | 140 | /** 141 | * @return ClassMetadataBuilder 142 | */ 143 | protected function newClassMetadataBuilder() 144 | { 145 | return new ClassMetadataBuilder( 146 | new ClassMetadataInfo($this->builder->getClassMetadata()->name) 147 | ); 148 | } 149 | 150 | /** 151 | * @param $builder 152 | * @param $source 153 | * 154 | * @return mixed 155 | */ 156 | protected function getAssociationBuilder(ClassMetadataBuilder $builder, array $source) 157 | { 158 | return new $this->relations[$source['type']]( 159 | $builder, 160 | $this->namingStrategy, 161 | $this->name, 162 | $source['targetEntity'] 163 | ); 164 | } 165 | 166 | /** 167 | * @param array $target 168 | * @param array $source 169 | * 170 | * @return array 171 | */ 172 | protected function mapJoinTable(array $target = [], array $source = []) 173 | { 174 | $joinTable['name'] = $target['name']; 175 | 176 | if ($this->hasJoinColumns($target)) { 177 | $joinTable['joinColumns'] = $this->mapJoinColumns( 178 | $target['joinColumns'], 179 | $source['joinColumns'] 180 | ); 181 | } 182 | 183 | if ($this->hasInverseJoinColumns($target)) { 184 | $joinTable['inverseJoinColumns'] = $this->mapJoinColumns( 185 | $target['inverseJoinColumns'], 186 | $source['inverseJoinColumns'] 187 | ); 188 | } 189 | 190 | return $joinTable; 191 | } 192 | 193 | /** 194 | * @param array $target 195 | * @param array $source 196 | * 197 | * @return mixed 198 | * 199 | * @internal param $target 200 | * @internal param $source 201 | * @internal param $overrideMapping 202 | */ 203 | protected function mapJoinColumns(array $target = [], array $source = []) 204 | { 205 | $joinColumns = []; 206 | foreach ($target as $index => $joinColumn) { 207 | if (isset($source[$index])) { 208 | $diff = array_diff($joinColumn, $source[$index]); 209 | 210 | if (!empty($diff)) { 211 | $joinColumns[] = $diff; 212 | } 213 | } 214 | } 215 | 216 | return $joinColumns; 217 | } 218 | 219 | /** 220 | * @param array $target 221 | * 222 | * @return bool 223 | */ 224 | protected function hasJoinColumns(array $target = []) 225 | { 226 | return isset($target['joinColumns']); 227 | } 228 | 229 | /** 230 | * @param array $target 231 | * 232 | * @return bool 233 | */ 234 | protected function hasInverseJoinColumns(array $target = []) 235 | { 236 | return isset($target['inverseJoinColumns']); 237 | } 238 | 239 | /** 240 | * @param array $target 241 | * 242 | * @return bool 243 | */ 244 | protected function hasJoinTable(array $target = []) 245 | { 246 | return isset($target['joinTable']); 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/Builders/Overrides/AttributeOverride.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 47 | $this->callback = $callback; 48 | $this->name = $name; 49 | $this->namingStrategy = $namingStrategy; 50 | } 51 | 52 | /** 53 | * Execute the build process. 54 | */ 55 | public function build() 56 | { 57 | $callback = $this->callback; 58 | 59 | // We will create a new class metadata builder instance, 60 | // so we can use it to easily generated a new mapping 61 | // array, without re-declaring the existing field 62 | $builder = $this->newClassMetadataBuilder(); 63 | 64 | $source = $this->convertToMappingArray($this->builder); 65 | 66 | // Create a new field builder for the new class metadata builder, 67 | // based on the existing (to be overridden) field 68 | $fieldBuilder = $this->getFieldBuilder( 69 | $builder, 70 | $source 71 | ); 72 | 73 | $field = $callback($fieldBuilder); 74 | 75 | // When the user forget to return, use the Field instance 76 | // which contains the same information 77 | $field = $field ?: $fieldBuilder; 78 | 79 | if (!$field instanceof Field) { 80 | throw new InvalidArgumentException('The callback should return an instance of '.Field::class); 81 | } 82 | 83 | $field->build(); 84 | 85 | $target = $this->convertToMappingArray($builder); 86 | 87 | $this->builder->getClassMetadata()->setAttributeOverride( 88 | $this->name, 89 | $this->mergeRecursively($source, $target) 90 | ); 91 | } 92 | 93 | /** 94 | * @param ClassMetadataBuilder $builder 95 | * @param array $mapping 96 | * 97 | * @return Field 98 | */ 99 | protected function getFieldBuilder(ClassMetadataBuilder $builder, array $mapping) 100 | { 101 | return Field::make( 102 | $builder, 103 | $mapping['type'], 104 | $this->name 105 | ); 106 | } 107 | 108 | /** 109 | * @param ClassMetadataBuilder $builder 110 | * 111 | * @throws \Doctrine\ORM\Mapping\MappingException 112 | * 113 | * @return array 114 | */ 115 | protected function convertToMappingArray(ClassMetadataBuilder $builder) 116 | { 117 | $metadata = $builder->getClassMetadata(); 118 | 119 | return $metadata->getFieldMapping($this->name); 120 | } 121 | 122 | /** 123 | * @return ClassMetadataBuilder 124 | */ 125 | protected function newClassMetadataBuilder() 126 | { 127 | return new ClassMetadataBuilder( 128 | new ClassMetadataInfo($this->builder->getClassMetadata()->name) 129 | ); 130 | } 131 | 132 | /** 133 | * Merges the field mappings recursively, by keeping originals 134 | * settings, but replacing and adding new once. 135 | * 136 | * @param array $source 137 | * @param array $target 138 | * 139 | * @return array 140 | */ 141 | protected function mergeRecursively(array $source, array $target) 142 | { 143 | foreach ($source as $key => $value) { 144 | if (!isset($target[$key])) { 145 | $target[$key] = $value; 146 | } elseif (is_array($value)) { 147 | $target[$key] = $this->mergeRecursively($value, $target[$key]); 148 | } 149 | } 150 | 151 | return $target; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/Builders/Overrides/Override.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 41 | $this->callback = $callback; 42 | $this->name = $name; 43 | $this->namingStrategy = $namingStrategy; 44 | } 45 | 46 | /** 47 | * Execute the build process. 48 | */ 49 | public function build() 50 | { 51 | $builder = OverrideBuilderFactory::create( 52 | $this->builder, 53 | $this->namingStrategy, 54 | $this->name, 55 | $this->callback 56 | ); 57 | 58 | $builder->build(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Builders/Overrides/OverrideBuilderFactory.php: -------------------------------------------------------------------------------- 1 | $check) { 23 | if ($check($builder->getClassMetadata(), $name)) { 24 | return new $buildable($builder, $namingStrategy, $name, $callback); 25 | } 26 | } 27 | 28 | throw new InvalidArgumentException('No attribute or association could be found for '.$name); 29 | } 30 | 31 | /** 32 | * @return array 33 | */ 34 | protected static function getFactories() 35 | { 36 | return [ 37 | AttributeOverride::class => function (ClassMetadata $meta, $name) { 38 | return $meta->hasField($name); 39 | }, 40 | AssociationOverride::class => function (ClassMetadata $meta, $name) { 41 | return $meta->hasAssociation($name); 42 | }, 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Builders/Primary.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 27 | $this->fields = $fields; 28 | } 29 | 30 | /** 31 | * Execute the build process. 32 | */ 33 | public function build() 34 | { 35 | $this->builder->getClassMetadata()->setIdentifier( 36 | $this->getFields() 37 | ); 38 | } 39 | 40 | /** 41 | * @return string[] 42 | */ 43 | public function getFields() 44 | { 45 | return $this->fields; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Builders/Table.php: -------------------------------------------------------------------------------- 1 | setName($name); 32 | } 33 | } 34 | 35 | /** 36 | * @param string $name 37 | * 38 | * @return $this 39 | */ 40 | public function setName($name) 41 | { 42 | $this->builder->setTable($name); 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * @param string $schema 49 | * 50 | * @return $this 51 | */ 52 | public function schema($schema) 53 | { 54 | $this->primaryTable['schema'] = $schema; 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * @param string $charset 61 | * 62 | * @return $this 63 | */ 64 | public function charset($charset) 65 | { 66 | $this->option('charset', $charset); 67 | 68 | return $this; 69 | } 70 | 71 | /** 72 | * @param string $collate 73 | * 74 | * @return $this 75 | */ 76 | public function collate($collate) 77 | { 78 | $this->option('collate', $collate); 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * @param array $options 85 | * 86 | * @return $this 87 | */ 88 | public function options(array $options = []) 89 | { 90 | $this->primaryTable['options'] = $options; 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * @param string $name 97 | * @param string $value 98 | * 99 | * @return $this 100 | */ 101 | public function option($name, $value) 102 | { 103 | $this->primaryTable['options'][$name] = $value; 104 | 105 | return $this; 106 | } 107 | 108 | /** 109 | * Execute the build process. 110 | */ 111 | public function build() 112 | { 113 | $this->builder->getClassMetadata()->setPrimaryTable($this->primaryTable); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Builders/Traits/Aliases.php: -------------------------------------------------------------------------------- 1 | disallowInEmbeddedClasses(); 13 | 14 | return $this->integer($name, $callback)->primary()->unsigned()->autoIncrement(); 15 | } 16 | 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | public function smallIncrements($name, callable $callback = null) 21 | { 22 | $this->disallowInEmbeddedClasses(); 23 | 24 | return $this->smallInteger($name, $callback)->primary()->unsigned()->autoIncrement(); 25 | } 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function bigIncrements($name, callable $callback = null) 31 | { 32 | $this->disallowInEmbeddedClasses(); 33 | 34 | return $this->bigInteger($name, $callback)->primary()->unsigned()->autoIncrement(); 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function unsignedSmallInteger($name, callable $callback = null) 41 | { 42 | return $this->smallInteger($name, $callback)->unsigned(); 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function unsignedInteger($name, callable $callback = null) 49 | { 50 | return $this->integer($name, $callback)->unsigned(); 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function unsignedBigInteger($name, callable $callback = null) 57 | { 58 | return $this->bigInteger($name, $callback)->unsigned(); 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | */ 64 | public function rememberToken($name = 'rememberToken', callable $callback = null) 65 | { 66 | return $this->string($name, $callback)->nullable()->length(100); 67 | } 68 | 69 | /** 70 | * @param string $message 71 | * 72 | * @throws \LogicException 73 | */ 74 | abstract protected function disallowInEmbeddedClasses($message = ''); 75 | 76 | /** 77 | * @param string $name 78 | * @param callable|null $callback 79 | * 80 | * @return \LaravelDoctrine\Fluent\Builders\Field 81 | */ 82 | abstract public function integer($name, callable $callback = null); 83 | 84 | /** 85 | * @param string $name 86 | * @param callable|null $callback 87 | * 88 | * @return \LaravelDoctrine\Fluent\Builders\Field 89 | */ 90 | abstract public function smallInteger($name, callable $callback = null); 91 | 92 | /** 93 | * @param string $name 94 | * @param callable|null $callback 95 | * 96 | * @return \LaravelDoctrine\Fluent\Builders\Field 97 | */ 98 | abstract public function bigInteger($name, callable $callback = null); 99 | 100 | /** 101 | * @param string $name 102 | * @param callable|null $callback 103 | * 104 | * @return \LaravelDoctrine\Fluent\Builders\Field 105 | */ 106 | abstract public function string($name, callable $callback = null); 107 | } 108 | -------------------------------------------------------------------------------- /src/Builders/Traits/Constraints.php: -------------------------------------------------------------------------------- 1 | constraint( 18 | Index::class, 19 | is_array($columns) ? $columns : func_get_args() 20 | ); 21 | } 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function primary($fields) 27 | { 28 | return $this->constraint( 29 | Primary::class, 30 | is_array($fields) ? $fields : func_get_args() 31 | ); 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function unique($columns) 38 | { 39 | return $this->constraint( 40 | UniqueConstraint::class, 41 | is_array($columns) ? $columns : func_get_args() 42 | ); 43 | } 44 | 45 | /** 46 | * @param string $class 47 | * @param array $columns 48 | * 49 | * @return mixed 50 | */ 51 | protected function constraint($class, array $columns) 52 | { 53 | $constraint = new $class($this->getBuilder(), $columns); 54 | 55 | $this->queue($constraint); 56 | 57 | return $constraint; 58 | } 59 | 60 | /** 61 | * @return \Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder 62 | */ 63 | abstract public function getBuilder(); 64 | 65 | /** 66 | * @param Buildable $buildable 67 | */ 68 | abstract protected function queue(Buildable $buildable); 69 | } 70 | -------------------------------------------------------------------------------- /src/Builders/Traits/Dates.php: -------------------------------------------------------------------------------- 1 | field(Types::DATE_MUTABLE, $name, $callback); 15 | } 16 | 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | public function dateTime($name, callable $callback = null) 21 | { 22 | return $this->field(Types::DATETIME_MUTABLE, $name, $callback); 23 | } 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function dateTimeTz($name, callable $callback = null) 29 | { 30 | return $this->field(Types::DATETIMETZ_MUTABLE, $name, $callback); 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function time($name, callable $callback = null) 37 | { 38 | return $this->field(Types::TIME_MUTABLE, $name, $callback); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function carbonDateTime($name, callable $callback = null) 45 | { 46 | return $this->field('carbondatetime', $name, $callback); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function carbonDateTimeTz($name, callable $callback = null) 53 | { 54 | return $this->field('carbondatetimetz', $name, $callback); 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | */ 60 | public function carbonDate($name, callable $callback = null) 61 | { 62 | return $this->field('carbondate', $name, $callback); 63 | } 64 | 65 | /** 66 | * {@inheritdoc} 67 | */ 68 | public function carbonTime($name, callable $callback = null) 69 | { 70 | return $this->field('carbontime', $name, $callback); 71 | } 72 | 73 | /** 74 | * {@inheritdoc} 75 | */ 76 | public function zendDate($name, callable $callback = null) 77 | { 78 | return $this->field('zenddate', $name, $callback); 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | */ 84 | public function timestamp($name, callable $callback = null) 85 | { 86 | return $this->carbonDateTime($name, $callback); 87 | } 88 | 89 | /** 90 | * {@inheritdoc} 91 | */ 92 | public function timestampTz($name, callable $callback = null) 93 | { 94 | return $this->carbonDateTimeTz($name, $callback); 95 | } 96 | 97 | /** 98 | * @param string $type 99 | * @param string $name 100 | * @param callable|null $callback 101 | * 102 | * @return \LaravelDoctrine\Fluent\Builders\Field 103 | */ 104 | abstract public function field($type, $name, callable $callback = null); 105 | } 106 | -------------------------------------------------------------------------------- /src/Builders/Traits/Fields.php: -------------------------------------------------------------------------------- 1 | getBuilder(), $type, $name); 17 | 18 | $this->callbackAndQueue($field, $callback); 19 | 20 | return $field; 21 | } 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function string($name, callable $callback = null) 27 | { 28 | return $this->field(Types::STRING, $name, $callback); 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function text($name, callable $callback = null) 35 | { 36 | return $this->field(Types::TEXT, $name, $callback); 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function integer($name, callable $callback = null) 43 | { 44 | return $this->field(Types::INTEGER, $name, $callback); 45 | } 46 | 47 | /** 48 | * {@inheritdoc} 49 | */ 50 | public function smallInteger($name, callable $callback = null) 51 | { 52 | return $this->field(Types::SMALLINT, $name, $callback); 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function bigInteger($name, callable $callback = null) 59 | { 60 | return $this->field(Types::BIGINT, $name, $callback); 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | public function guid($name, callable $callback = null) 67 | { 68 | return $this->field(Types::GUID, $name, $callback); 69 | } 70 | 71 | /** 72 | * {@inheritdoc} 73 | */ 74 | public function blob($name, callable $callback = null) 75 | { 76 | return $this->field(Types::BLOB, $name, $callback); 77 | } 78 | 79 | /** 80 | * {@inheritdoc} 81 | */ 82 | public function object($name, callable $callback = null) 83 | { 84 | return $this->field(Types::OBJECT, $name, $callback); 85 | } 86 | 87 | /** 88 | * {@inheritdoc} 89 | */ 90 | public function float($name, callable $callback = null) 91 | { 92 | return $this->field(Types::FLOAT, $name, $callback)->precision(8)->scale(2); 93 | } 94 | 95 | /** 96 | * {@inheritdoc} 97 | */ 98 | public function decimal($name, callable $callback = null) 99 | { 100 | return $this->field(Types::DECIMAL, $name, $callback)->precision(8)->scale(2); 101 | } 102 | 103 | /** 104 | * {@inheritdoc} 105 | */ 106 | public function boolean($name, callable $callback = null) 107 | { 108 | return $this->field(Types::BOOLEAN, $name, $callback); 109 | } 110 | 111 | /** 112 | * {@inheritdoc} 113 | */ 114 | public function simpleArray($name, callable $callback = null) 115 | { 116 | return $this->field(Types::SIMPLE_ARRAY, $name, $callback); 117 | } 118 | 119 | /** 120 | * {@inheritdoc} 121 | */ 122 | public function jsonArray($name, callable $callback = null) 123 | { 124 | return $this->field(Types::JSON, $name, $callback); 125 | } 126 | 127 | /** 128 | * {@inheritdoc} 129 | */ 130 | public function binary($name, callable $callback = null) 131 | { 132 | return $this->field(Types::BINARY, $name, $callback)->nullable(); 133 | } 134 | 135 | /** 136 | * @return \Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder 137 | */ 138 | abstract public function getBuilder(); 139 | 140 | /** 141 | * @param Buildable $buildable 142 | * @param callable|null $callback 143 | */ 144 | abstract protected function callbackAndQueue(Buildable $buildable, callable $callback = null); 145 | } 146 | -------------------------------------------------------------------------------- /src/Builders/Traits/Macroable.php: -------------------------------------------------------------------------------- 1 | getMacro($method), $params); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Builders/Traits/Queueable.php: -------------------------------------------------------------------------------- 1 | queued[spl_object_hash($buildable)] = $buildable; 21 | } 22 | 23 | /** 24 | * @param Buildable $buildable 25 | * @param callable|null $callback 26 | */ 27 | public function callbackAndQueue(Buildable $buildable, callable $callback = null) 28 | { 29 | $this->callIfCallable($callback, $buildable); 30 | 31 | $this->queue($buildable); 32 | } 33 | 34 | /** 35 | * Execute the build process for all queued buildables. 36 | */ 37 | public function build() 38 | { 39 | /** @var Buildable[] $delayed */ 40 | $delayed = []; 41 | 42 | foreach ($this->getQueued() as $buildable) { 43 | if ($buildable instanceof Delay) { 44 | $delayed[] = $buildable; 45 | } else { 46 | $buildable->build(); 47 | } 48 | } 49 | 50 | foreach ($delayed as $buildable) { 51 | $buildable->build(); 52 | } 53 | } 54 | 55 | /** 56 | * @return \LaravelDoctrine\Fluent\Buildable[] 57 | */ 58 | public function getQueued() 59 | { 60 | return $this->queued; 61 | } 62 | 63 | /** 64 | * Call the callable... only if it is really one. 65 | * 66 | * @param callable|null $callback 67 | * @param mixed $builder 68 | * 69 | * @return void 70 | */ 71 | protected function callIfCallable($callback, $builder) 72 | { 73 | if (is_callable($callback)) { 74 | $callback($builder); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Builders/Traits/QueuesMacros.php: -------------------------------------------------------------------------------- 1 | callMacro($method, $args); 33 | 34 | if ($result instanceof Buildable) { 35 | $this->queue($result); 36 | } 37 | 38 | return $result; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Builders/Traits/Relations.php: -------------------------------------------------------------------------------- 1 | oneToOne($entity, $field, $callback)->ownedBy( 21 | $this->guessSingularField($entity) 22 | ); 23 | } 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function oneToOne($entity, $field = null, callable $callback = null) 29 | { 30 | return $this->addRelation( 31 | new OneToOne( 32 | $this->getBuilder(), 33 | $this->getNamingStrategy(), 34 | $this->guessSingularField($entity, $field), 35 | $entity 36 | ), 37 | $callback 38 | ); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function belongsTo($entity, $field = null, callable $callback = null) 45 | { 46 | return $this->manyToOne($entity, $field, $callback); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function manyToOne($entity, $field = null, callable $callback = null) 53 | { 54 | return $this->addRelation( 55 | new ManyToOne( 56 | $this->getBuilder(), 57 | $this->getNamingStrategy(), 58 | $this->guessSingularField($entity, $field), 59 | $entity 60 | ), 61 | $callback 62 | ); 63 | } 64 | 65 | /** 66 | * {@inheritdoc} 67 | */ 68 | public function hasMany($entity, $field = null, callable $callback = null) 69 | { 70 | return $this->oneToMany($entity, $field, $callback); 71 | } 72 | 73 | /** 74 | * {@inheritdoc} 75 | */ 76 | public function oneToMany($entity, $field = null, callable $callback = null) 77 | { 78 | return $this->addRelation( 79 | new OneToMany( 80 | $this->getBuilder(), 81 | $this->getNamingStrategy(), 82 | $this->guessPluralField($entity, $field), 83 | $entity 84 | ), 85 | $callback 86 | ); 87 | } 88 | 89 | /** 90 | * {@inheritdoc} 91 | */ 92 | public function belongsToMany($entity, $field = null, callable $callback = null) 93 | { 94 | return $this->manyToMany($entity, $field, $callback); 95 | } 96 | 97 | /** 98 | * {@inheritdoc} 99 | */ 100 | public function manyToMany($entity, $field = null, callable $callback = null) 101 | { 102 | return $this->addRelation( 103 | new ManyToMany( 104 | $this->getBuilder(), 105 | $this->getNamingStrategy(), 106 | $this->guessPluralField($entity, $field), 107 | $entity 108 | ), 109 | $callback 110 | ); 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | */ 116 | public function addRelation(Relation $relation, callable $callback = null) 117 | { 118 | $this->callbackAndQueue($relation, $callback); 119 | 120 | return $relation; 121 | } 122 | 123 | /** 124 | * @param string $entity 125 | * @param string|null $field 126 | * 127 | * @return string 128 | */ 129 | protected function guessSingularField($entity, $field = null) 130 | { 131 | return $field ?: (InflectorFactory::create()->build())->singularize( 132 | lcfirst(basename(str_replace('\\', '/', $entity))) 133 | ); 134 | } 135 | 136 | /** 137 | * @param string $entity 138 | * @param string|null $field 139 | * 140 | * @return string 141 | */ 142 | protected function guessPluralField($entity, $field = null) 143 | { 144 | return $field ?: (InflectorFactory::create()->build())->pluralize($this->guessSingularField($entity)); 145 | } 146 | 147 | /** 148 | * @return \Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder 149 | */ 150 | abstract public function getBuilder(); 151 | 152 | /** 153 | * @param Buildable $buildable 154 | * @param callable|null $callback 155 | */ 156 | abstract protected function callbackAndQueue(Buildable $buildable, callable $callback = null); 157 | 158 | /** 159 | * @return \Doctrine\ORM\Mapping\NamingStrategy 160 | */ 161 | abstract public function getNamingStrategy(); 162 | } 163 | -------------------------------------------------------------------------------- /src/Builders/UniqueConstraint.php: -------------------------------------------------------------------------------- 1 | builder->addUniqueConstraint( 22 | $this->getColumns(), 23 | $this->getName() 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/EmbeddableMapping.php: -------------------------------------------------------------------------------- 1 | addMapper($this->mapFor(), new EmbeddableMapper($this)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/EntityMapping.php: -------------------------------------------------------------------------------- 1 | addMapper($this->mapFor(), new EntityMapper($this)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Extensions/ExtensibleClassMetadata.php: -------------------------------------------------------------------------------- 1 | extensions[$name] = $config; 27 | } 28 | 29 | /** 30 | * @param string $name 31 | * 32 | * @return array 33 | */ 34 | public function getExtension($name) 35 | { 36 | if (isset($this->extensions[$name])) { 37 | return $this->extensions[$name]; 38 | } 39 | 40 | return []; 41 | } 42 | 43 | /** 44 | * Merge with current extension configuration, appending new values to old ones. 45 | * 46 | * @param string $name 47 | * @param array $config 48 | */ 49 | public function appendExtension($name, array $config = []) 50 | { 51 | $merged = array_merge_recursive( 52 | $this->getExtension($name), 53 | $config 54 | ); 55 | 56 | $this->addExtension($name, $merged); 57 | } 58 | 59 | /** 60 | * Merge with current extension configuration, overwriting with new values. 61 | * 62 | * @param string $name 63 | * @param array $config 64 | * 65 | * @return void 66 | */ 67 | public function mergeExtension($name, array $config) 68 | { 69 | $this->addExtension($name, array_merge( 70 | $this->getExtension($name), 71 | $config 72 | )); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Extensions/ExtensibleClassMetadataFactory.php: -------------------------------------------------------------------------------- 1 | entityManager = $em; 25 | } 26 | 27 | /** 28 | * Override to implement our custom ClassMetadata object. 29 | * 30 | * {@inheritdoc} 31 | */ 32 | protected function newClassMetadataInstance($className) 33 | { 34 | return new ExtensibleClassMetadata($className, $this->entityManager->getConfiguration()->getNamingStrategy()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Extensions/Extension.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 49 | $this->fieldName = $fieldName; 50 | } 51 | 52 | /** 53 | * @return $this 54 | */ 55 | public function onCreate() 56 | { 57 | return $this->on('create'); 58 | } 59 | 60 | /** 61 | * @return $this 62 | */ 63 | public function onUpdate() 64 | { 65 | return $this->on('update'); 66 | } 67 | 68 | /** 69 | * @param array|string|null $fields 70 | * @param string|null $value 71 | * 72 | * @return $this 73 | */ 74 | public function onChange($fields = null, $value = null) 75 | { 76 | return $this->on('change', $fields, $value); 77 | } 78 | 79 | /** 80 | * Execute the build process. 81 | */ 82 | public function build() 83 | { 84 | if ($this->on === null) { 85 | throw new InvalidMappingException( 86 | "Field - [{$this->fieldName}] trigger 'on' is not one of [update, create, change] in class - {$this->classMetadata->name}" 87 | ); 88 | } 89 | 90 | if (is_array($this->trackedFields) && $this->value !== null) { 91 | throw new InvalidMappingException('Extension does not support multiple value change-set detection yet.'); 92 | } 93 | 94 | $this->classMetadata->appendExtension($this->getExtensionName(), [ 95 | $this->on => [ 96 | $this->makeConfiguration(), 97 | ], 98 | ]); 99 | } 100 | 101 | /** 102 | * @param string $on 103 | * @param array|string|null $fields 104 | * @param string|null $value 105 | * 106 | * @return $this 107 | */ 108 | protected function on($on, $fields = null, $value = null) 109 | { 110 | $this->on = $on; 111 | $this->trackedFields = $fields; 112 | $this->value = $value; 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * Returns either the field name on "create" and "update", or the array configuration on "change". 119 | * 120 | * @return array|string 121 | */ 122 | protected function makeConfiguration() 123 | { 124 | if ($this->on == 'create' || $this->on == 'update') { 125 | return $this->fieldName; 126 | } 127 | 128 | return [ 129 | 'field' => $this->fieldName, 130 | 'trackedField' => $this->trackedFields, 131 | 'value' => $this->value, 132 | ]; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Blameable.php: -------------------------------------------------------------------------------- 1 | getClassMetadata(), $builder->getName()); 24 | }); 25 | 26 | ManyToOne::macro(self::MACRO_METHOD, function (ManyToOne $builder) { 27 | return new static($builder->getClassMetadata(), $builder->getRelation()); 28 | }); 29 | } 30 | 31 | /** 32 | * Return the name of the actual extension. 33 | * 34 | * @return string 35 | */ 36 | protected function getExtensionName() 37 | { 38 | return Fluent::EXTENSION_NAME; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/ClosureTable.php: -------------------------------------------------------------------------------- 1 | closureClass = $class; 28 | } 29 | 30 | /** 31 | * Execute the build process. 32 | */ 33 | public function build() 34 | { 35 | $this->builder->entity()->setRepositoryClass(ClosureTreeRepository::class); 36 | 37 | parent::build(); 38 | } 39 | 40 | /** 41 | * {@inheritdoc} 42 | */ 43 | protected function getValues() 44 | { 45 | return array_merge(parent::getValues(), [ 46 | 'strategy' => 'closure', 47 | 'closure' => $this->closureClass, 48 | ]); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/GedmoBuilderHints.php: -------------------------------------------------------------------------------- 1 | getClassMetadata(), $builder->getName()); 21 | }); 22 | } 23 | 24 | /** 25 | * Return the name of the actual extension. 26 | * 27 | * @return string 28 | */ 29 | protected function getExtensionName() 30 | { 31 | return Fluent::EXTENSION_NAME; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Locale.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 36 | $this->fieldName = $fieldName; 37 | } 38 | 39 | /** 40 | * @return void 41 | */ 42 | public static function enable() 43 | { 44 | Builder::macro(self::MACRO_METHOD, function (Builder $builder, $fieldName) { 45 | return new static($builder->getClassMetadata(), $fieldName); 46 | }); 47 | } 48 | 49 | /** 50 | * Execute the build process. 51 | */ 52 | public function build() 53 | { 54 | if ($this->classMetadata->hasField($this->fieldName)) { 55 | throw new InvalidMappingException( 56 | "Locale field [{$this->fieldName}] should not be mapped as column property in entity - {$this->classMetadata->name}, since it makes no sense" 57 | ); 58 | } 59 | 60 | $this->classMetadata->appendExtension($this->getExtensionName(), [ 61 | 'locale' => $this->fieldName, 62 | ]); 63 | } 64 | 65 | /** 66 | * Return the name of the actual extension. 67 | * 68 | * @return string 69 | */ 70 | public function getExtensionName() 71 | { 72 | return FluentDriver::EXTENSION_NAME; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Loggable.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 30 | $this->logEntry = $logEntry; 31 | } 32 | 33 | /** 34 | * @return void 35 | */ 36 | public static function enable() 37 | { 38 | Builder::macro('loggable', function (Builder $builder, $logEntry = null) { 39 | $loggable = new static($builder->getClassMetadata(), $logEntry); 40 | $loggable->build(); 41 | }); 42 | 43 | Versioned::enable(); 44 | } 45 | 46 | /** 47 | * Execute the build process. 48 | */ 49 | public function build() 50 | { 51 | $config = [ 52 | 'loggable' => true, 53 | ]; 54 | 55 | if ($this->logEntry !== null) { 56 | $config['logEntryClass'] = $this->logEntry; 57 | } 58 | 59 | $this->classMetadata->addExtension(Fluent::EXTENSION_NAME, array_merge( 60 | $this->classMetadata->getExtension(Fluent::EXTENSION_NAME), 61 | $config 62 | )); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Mappings/Loggable/AbstractLogEntryMapping.php: -------------------------------------------------------------------------------- 1 | increments('id'); 25 | $builder->string('action')->length(8); 26 | $builder->dateTime('loggedAt')->name('logged_at'); 27 | $builder->string('objectId')->name('object_id')->length(64)->nullable(); 28 | $builder->string('objectClass')->name('object_class'); 29 | $builder->integer('version'); 30 | $builder->array('data')->nullable(); 31 | $builder->string('username')->nullable(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Mappings/Loggable/LogEntryMapping.php: -------------------------------------------------------------------------------- 1 | table('ext_log_entries'); 26 | $builder->entity()->setRepositoryClass(LogEntryRepository::class); 27 | 28 | $builder->index(['object_class'])->name('log_class_lookup_idx'); 29 | $builder->index(['logged_at'])->name('log_date_lookup_idx'); 30 | $builder->index(['username'])->name('log_user_lookup_idx'); 31 | $builder->index(['object_id', 'object_class', 'version'])->name('log_version_lookup_idx'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Mappings/Translatable/AbstractPersonalTranslationMapping.php: -------------------------------------------------------------------------------- 1 | integer('id')->unsigned()->primary()->generatedValue(function (GeneratedValue $builder) { 26 | $builder->identity(); 27 | }); 28 | $builder->string('locale')->length(8); 29 | $builder->string('field')->length(32); 30 | $builder->text('content')->nullable(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Mappings/Translatable/AbstractTranslationMapping.php: -------------------------------------------------------------------------------- 1 | integer('id')->unsigned()->primary()->generatedValue(function (GeneratedValue $builder) { 26 | $builder->identity(); 27 | }); 28 | $builder->string('locale')->length(8); 29 | $builder->string('objectClass')->name('object_class'); 30 | $builder->string('field')->length(32); 31 | $builder->string('foreignKey')->length(64)->name('foreign_key'); 32 | $builder->text('content')->nullable(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Mappings/Translatable/TranslationMapping.php: -------------------------------------------------------------------------------- 1 | table('ext_translations'); 26 | $builder->entity()->setRepositoryClass(TranslationRepository::class); 27 | 28 | $builder->index(['locale', 'object_class', 'foreign_key'])->name('translations_lookup_idx'); 29 | $builder->unique(['locale', 'object_class', 'field', 'foreign_key'])->name('lookup_unique_idx'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Mappings/Tree/AbstractClosureMapping.php: -------------------------------------------------------------------------------- 1 | integer('id')->unsigned()->primary()->generatedValue(function (GeneratedValue $builder) { 26 | $builder->identity(); 27 | }); 28 | $builder->integer('depth'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/MaterializedPath.php: -------------------------------------------------------------------------------- 1 | mapField('string', $field, null, true); 73 | 74 | $this->path = new TreePath($this->getClassMetadata(), $field, $separator); 75 | 76 | $this->callbackAndQueue($this->path, $callback); 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * @param string $hash 83 | * 84 | * @return $this 85 | */ 86 | public function pathHash($hash = 'pathHash') 87 | { 88 | $this->mapField('string', $hash); 89 | 90 | $this->hash = $hash; 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * @param string $field 97 | * 98 | * @return $this 99 | */ 100 | public function pathSource($field = 'id') 101 | { 102 | $this->source = $field; 103 | 104 | return $this; 105 | } 106 | 107 | /** 108 | * Execute the build process. 109 | */ 110 | public function build() 111 | { 112 | parent::build(); 113 | 114 | $this->buildQueue(); 115 | } 116 | 117 | /** 118 | * Add default values to all required fields. 119 | * 120 | * @return void 121 | */ 122 | protected function defaults() 123 | { 124 | parent::defaults(); 125 | 126 | if ($this->isMissingPath()) { 127 | $this->path(); 128 | } 129 | 130 | if ($this->isMissingSource()) { 131 | $this->pathSource(); 132 | } 133 | } 134 | 135 | /** 136 | * @return array 137 | */ 138 | protected function getValues() 139 | { 140 | $values = array_merge(parent::getValues(), [ 141 | 'strategy' => 'materializedPath', 142 | 'path_source' => $this->source, 143 | 'path_separator' => $this->separator, 144 | 'path_append_id' => $this->appendIds, 145 | 'path_starts_with_separator' => $this->startsWithSeparator, 146 | 'path_ends_with_separator' => $this->endsWithSeparator, 147 | 'activate_locking' => false, 148 | ]); 149 | 150 | if ($this->hash) { 151 | $values['path_hash'] = $this->hash; 152 | } 153 | 154 | return $values; 155 | } 156 | 157 | /** 158 | * @return bool 159 | */ 160 | private function isMissingPath() 161 | { 162 | return !$this->alreadyConfigured('path') && !$this->path; 163 | } 164 | 165 | /** 166 | * @return bool 167 | */ 168 | private function isMissingSource() 169 | { 170 | return !$this->alreadyConfigured('path_source') && !$this->source; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/NestedSet.php: -------------------------------------------------------------------------------- 1 | validateNumericField($type, $field); 51 | 52 | $this->mapField($type, $field, $callback); 53 | 54 | $this->left = $field; 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * @param string $field 61 | * @param string $type 62 | * @param callable|null $callback 63 | * 64 | * @throws InvalidMappingException 65 | * 66 | * @return $this 67 | */ 68 | public function right($field = 'right', $type = 'integer', callable $callback = null) 69 | { 70 | $this->validateNumericField($type, $field); 71 | 72 | $this->mapField($type, $field, $callback); 73 | 74 | $this->right = $field; 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * @param string $field 81 | * @param callable|null $callback 82 | * 83 | * @return $this 84 | */ 85 | public function root($field = 'root', callable $callback = null) 86 | { 87 | $this->addSelfReferencingRelation($field, $callback); 88 | 89 | $this->root = $field; 90 | 91 | return $this; 92 | } 93 | 94 | /** 95 | * Execute the build process. 96 | */ 97 | public function build() 98 | { 99 | $this->builder->entity()->setRepositoryClass(NestedTreeRepository::class); 100 | 101 | parent::build(); 102 | } 103 | 104 | /** 105 | * Add default values to all required fields. 106 | * 107 | * @return void 108 | */ 109 | protected function defaults() 110 | { 111 | parent::defaults(); 112 | 113 | if ($this->isMissingLeft()) { 114 | $this->left(); 115 | } 116 | 117 | if ($this->isMissingRight()) { 118 | $this->right(); 119 | } 120 | } 121 | 122 | protected function getValues() 123 | { 124 | return array_merge(parent::getValues(), [ 125 | 'strategy' => 'nested', 126 | 'left' => $this->left, 127 | 'right' => $this->right, 128 | 'root' => $this->root, 129 | ]); 130 | } 131 | 132 | /** 133 | * @return bool 134 | */ 135 | private function isMissingLeft() 136 | { 137 | return !$this->alreadyConfigured('left') && !$this->left; 138 | } 139 | 140 | /** 141 | * @return bool 142 | */ 143 | private function isMissingRight() 144 | { 145 | return !$this->alreadyConfigured('right') && !$this->right; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Sluggable.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 99 | $this->fieldName = $fieldName; 100 | $this->baseOn($fields); 101 | } 102 | 103 | /** 104 | * Return the name of the actual extension. 105 | * 106 | * @return string 107 | */ 108 | public function getExtensionName() 109 | { 110 | return FluentDriver::EXTENSION_NAME; 111 | } 112 | 113 | /** 114 | * @return void 115 | */ 116 | public static function enable() 117 | { 118 | Field::macro(static::MACRO_METHOD, function (Field $builder, $fields) { 119 | return new static($builder->getClassMetadata(), $builder->getName(), $fields); 120 | }); 121 | } 122 | 123 | /** 124 | * Execute the build process. 125 | */ 126 | public function build() 127 | { 128 | $this->isValidField($this->classMetadata, $this->fieldName); 129 | 130 | $this->classMetadata->appendExtension($this->getExtensionName(), [ 131 | 'slugs' => [ 132 | $this->fieldName => $this->makeConfiguration(), 133 | ], 134 | ]); 135 | } 136 | 137 | /** 138 | * @param array|string $fields 139 | * 140 | * @return Sluggable 141 | */ 142 | public function baseOn($fields) 143 | { 144 | $this->fields = is_array($fields) ? $fields : [$fields]; 145 | 146 | return $this; 147 | } 148 | 149 | /** 150 | * @param array|string $handlers 151 | * 152 | * @return Sluggable 153 | */ 154 | public function handlers($handlers) 155 | { 156 | $this->handlers = is_array($handlers) ? $handlers : [$handlers]; 157 | 158 | return $this; 159 | } 160 | 161 | /** 162 | * @param string $style 163 | * 164 | * @return Sluggable 165 | */ 166 | public function style($style) 167 | { 168 | $this->style = $style; 169 | 170 | return $this; 171 | } 172 | 173 | /** 174 | * @param string $dateFormat 175 | * 176 | * @return Sluggable 177 | */ 178 | public function dateFormat($dateFormat) 179 | { 180 | $this->dateFormat = $dateFormat; 181 | 182 | return $this; 183 | } 184 | 185 | /** 186 | * @param bool $updatable 187 | * 188 | * @return Sluggable 189 | */ 190 | public function updatable($updatable = true) 191 | { 192 | $this->updatable = $updatable; 193 | 194 | return $this; 195 | } 196 | 197 | /** 198 | * @param bool $unique 199 | * 200 | * @return Sluggable 201 | */ 202 | public function unique($unique = true) 203 | { 204 | $this->unique = $unique; 205 | 206 | return $this; 207 | } 208 | 209 | /** 210 | * @param null $unique_base 211 | * 212 | * @return Sluggable 213 | */ 214 | public function uniqueBase($unique_base) 215 | { 216 | $this->unique_base = $unique_base; 217 | 218 | return $this; 219 | } 220 | 221 | /** 222 | * @param string $separator 223 | * 224 | * @return Sluggable 225 | */ 226 | public function separator($separator) 227 | { 228 | $this->separator = $separator; 229 | 230 | return $this; 231 | } 232 | 233 | /** 234 | * @param string $prefix 235 | * 236 | * @return Sluggable 237 | */ 238 | public function prefix($prefix) 239 | { 240 | $this->prefix = $prefix; 241 | 242 | return $this; 243 | } 244 | 245 | /** 246 | * @param string $suffix 247 | * 248 | * @return Sluggable 249 | */ 250 | public function suffix($suffix) 251 | { 252 | $this->suffix = $suffix; 253 | 254 | return $this; 255 | } 256 | 257 | /** 258 | * Checks if $field type is valid as Sluggable field. 259 | * 260 | * @param ClassMetadataInfo $meta 261 | * @param string $field 262 | * 263 | * @throws InvalidArgumentException 264 | * 265 | * @return bool 266 | */ 267 | protected function isValidField(ClassMetadataInfo $meta, $field) 268 | { 269 | $mapping = $meta->getFieldMapping($field); 270 | 271 | if (!$mapping || !in_array($mapping['type'], $this->validTypes)) { 272 | throw new InvalidArgumentException('Sluggable field is not a valid field type'); 273 | } 274 | 275 | return true; 276 | } 277 | 278 | /** 279 | * @return array 280 | */ 281 | private function makeConfiguration() 282 | { 283 | return [ 284 | 'fields' => $this->fields, 285 | 'handlers' => $this->handlers, 286 | 'slug' => $this->fieldName, 287 | 'style' => $this->style, 288 | 'dateFormat' => $this->dateFormat, 289 | 'updatable' => $this->updatable, 290 | 'unique' => $this->unique, 291 | 'unique_base' => $this->unique_base, 292 | 'separator' => $this->separator, 293 | 'prefix' => $this->prefix, 294 | 'suffix' => $this->suffix, 295 | ]; 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/SoftDeleteable.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 43 | $this->fieldName = $fieldName; 44 | } 45 | 46 | /** 47 | * Return the name of the actual extension. 48 | * 49 | * @return string 50 | */ 51 | public function getExtensionName() 52 | { 53 | return FluentDriver::EXTENSION_NAME; 54 | } 55 | 56 | /** 57 | * @param bool $value 58 | * 59 | * @return $this 60 | */ 61 | public function timeAware($value = true) 62 | { 63 | $this->timeAware = $value; 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * @return void 70 | */ 71 | public static function enable() 72 | { 73 | Builder::macro(static::MACRO_METHOD, function (Builder $builder, $fieldName = 'deletedAt', $type = 'dateTime') { 74 | $builder->{$type}($fieldName)->nullable(); 75 | 76 | return new static($builder->getClassMetadata(), $fieldName); 77 | }); 78 | 79 | Field::macro(static::MACRO_METHOD, function (Field $builder) { 80 | return new static($builder->getClassMetadata(), $builder->getName()); 81 | }); 82 | } 83 | 84 | /** 85 | * Execute the build process. 86 | */ 87 | public function build() 88 | { 89 | $this->classMetadata->addExtension($this->getExtensionName(), [ 90 | 'softDeleteable' => true, 91 | 'fieldName' => $this->fieldName, 92 | 'timeAware' => $this->timeAware, 93 | ]); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Sortable.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 33 | $this->fieldName = $fieldName; 34 | } 35 | 36 | /** 37 | * Return the name of the actual extension. 38 | * 39 | * @return string 40 | */ 41 | public function getExtensionName() 42 | { 43 | return FluentDriver::EXTENSION_NAME; 44 | } 45 | 46 | /** 47 | * @return void 48 | */ 49 | public static function enable() 50 | { 51 | Field::macro(self::MACRO_METHOD, function (Field $builder) { 52 | return new static($builder->getClassMetadata(), $builder->getName()); 53 | }); 54 | 55 | ManyToOne::macro(self::MACRO_METHOD, function (ManyToOne $builder) { 56 | return new static($builder->getClassMetadata(), $builder->getRelation()); 57 | }); 58 | 59 | ManyToMany::macro(self::MACRO_METHOD, function (ManyToMany $builder) { 60 | return new static($builder->getClassMetadata(), $builder->getRelation()); 61 | }); 62 | } 63 | 64 | /** 65 | * Execute the build process. 66 | */ 67 | public function build() 68 | { 69 | $this->classMetadata->appendExtension($this->getExtensionName(), [ 70 | 'groups' => [ 71 | $this->fieldName, 72 | ], 73 | ]); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/SortablePosition.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 31 | $this->fieldName = $fieldName; 32 | } 33 | 34 | /** 35 | * Return the name of the actual extension. 36 | * 37 | * @return string 38 | */ 39 | public function getExtensionName() 40 | { 41 | return FluentDriver::EXTENSION_NAME; 42 | } 43 | 44 | /** 45 | * @return void 46 | */ 47 | public static function enable() 48 | { 49 | Field::macro(self::MACRO_METHOD, function (Field $builder) { 50 | return new static($builder->getClassMetadata(), $builder->getName()); 51 | }); 52 | } 53 | 54 | /** 55 | * Execute the build process. 56 | */ 57 | public function build() 58 | { 59 | $this->classMetadata->appendExtension($this->getExtensionName(), [ 60 | 'position' => $this->fieldName, 61 | ]); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Timestampable.php: -------------------------------------------------------------------------------- 1 | getClassMetadata(), $builder->getName()); 23 | }); 24 | 25 | Timestamps::enable(); 26 | } 27 | 28 | /** 29 | * Return the name of the actual extension. 30 | * 31 | * @return string 32 | */ 33 | protected function getExtensionName() 34 | { 35 | return FluentDriver::EXTENSION_NAME; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Timestamps.php: -------------------------------------------------------------------------------- 1 | {$type}($createdAt)->timestampable()->onCreate(); 21 | $builder->{$type}($updatedAt)->timestampable()->onUpdate(); 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Translatable.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 32 | $this->fieldName = $fieldName; 33 | } 34 | 35 | /** 36 | * Return the name of the actual extension. 37 | * 38 | * @return string 39 | */ 40 | public function getExtensionName() 41 | { 42 | return FluentDriver::EXTENSION_NAME; 43 | } 44 | 45 | /** 46 | * @return void 47 | */ 48 | public static function enable() 49 | { 50 | Field::macro(static::MACRO_METHOD, function (Field $builder) { 51 | return new static($builder->getClassMetadata(), $builder->getName()); 52 | }); 53 | 54 | Locale::enable(); 55 | TranslationClass::enable(); 56 | } 57 | 58 | /** 59 | * Execute the build process. 60 | */ 61 | public function build() 62 | { 63 | $this->classMetadata->appendExtension($this->getExtensionName(), [ 64 | 'fields' => [ 65 | $this->fieldName, 66 | ], 67 | ]); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/TranslationClass.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 33 | $this->class = $class; 34 | } 35 | 36 | /** 37 | * @return void 38 | */ 39 | public static function enable() 40 | { 41 | Builder::macro(self::MACRO_METHOD, function (Builder $builder, $class) { 42 | return new static($builder->getClassMetadata(), $class); 43 | }); 44 | } 45 | 46 | /** 47 | * Execute the build process. 48 | */ 49 | public function build() 50 | { 51 | $this->classMetadata->appendExtension($this->getExtensionName(), [ 52 | 'translationClass' => $this->class, 53 | ]); 54 | } 55 | 56 | /** 57 | * Return the name of the actual extension. 58 | * 59 | * @return string 60 | */ 61 | public function getExtensionName() 62 | { 63 | return FluentDriver::EXTENSION_NAME; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Tree.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 30 | } 31 | 32 | /** 33 | * Enable extension. 34 | */ 35 | public static function enable() 36 | { 37 | Builder::macro(self::MACRO_METHOD, function (Fluent $builder, callable $callback = null) { 38 | $tree = new static($builder); 39 | 40 | if (is_callable($callback)) { 41 | call_user_func($callback, $tree); 42 | } 43 | 44 | return $tree; 45 | }); 46 | 47 | NestedSet::enable(); 48 | MaterializedPath::enable(); 49 | ClosureTable::enable(); 50 | } 51 | 52 | /** 53 | * Sets the tree strategy to NestedSet, returning it for further customization. 54 | * See {@link https://en.wikipedia.org/wiki/Nested_set_model} for more information on nested set trees. 55 | * 56 | * @return NestedSet 57 | */ 58 | public function asNestedSet() 59 | { 60 | return $this->strategy(new NestedSet($this->builder)); 61 | } 62 | 63 | /** 64 | * Sets the tree strategy to MaterializedPath, returning it for further customization. 65 | * See {@link https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/} for 66 | * more information on materialized path trees. 67 | * 68 | * @return MaterializedPath 69 | */ 70 | public function asMaterializedPath() 71 | { 72 | return $this->strategy(new MaterializedPath($this->builder)); 73 | } 74 | 75 | /** 76 | * Sets the tree strategy to ClosureTable, returning it for further customization. 77 | * See {@link https://coderwall.com/p/lixing/closure-tables-for-browsing-trees-in-sql} for more 78 | * information on closure table trees. 79 | * 80 | * @param string $class The class that represents the ClosureTable. You may extend Gedmo's mapped superclass 81 | * for easier usage. {@see Gedmo\Tree\Entity\MappedSuperclass\AbstractClosure} 82 | * 83 | * @return ClosureTable 84 | */ 85 | public function asClosureTable($class) 86 | { 87 | return $this->strategy(new ClosureTable($this->builder, $class)); 88 | } 89 | 90 | /** 91 | * Execute the build process. 92 | */ 93 | public function build() 94 | { 95 | $this->strategy->build(); 96 | } 97 | 98 | /** 99 | * @param Buildable $strategy 100 | * 101 | * @return Buildable 102 | */ 103 | protected function strategy(Buildable $strategy) 104 | { 105 | return $this->strategy = $strategy; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/TreeLeft.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 33 | $this->fieldName = $fieldName; 34 | } 35 | 36 | /** 37 | * Enable TreeLeft. 38 | */ 39 | public static function enable() 40 | { 41 | Field::macro(self::MACRO_METHOD, function (Field $field) { 42 | return new static($field->getClassMetadata(), $field->getName()); 43 | }); 44 | } 45 | 46 | /** 47 | * Execute the build process. 48 | */ 49 | public function build() 50 | { 51 | if (!(new Validator())->isValidField($this->classMetadata, $this->fieldName)) { 52 | throw new InvalidMappingException("Tree left field must be 'integer' in class - {$this->classMetadata->name}"); 53 | } 54 | 55 | $this->classMetadata->mergeExtension($this->getExtensionName(), [ 56 | 'left' => $this->fieldName, 57 | ]); 58 | } 59 | 60 | /** 61 | * Return the name of the actual extension. 62 | * 63 | * @return string 64 | */ 65 | public function getExtensionName() 66 | { 67 | return FluentDriver::EXTENSION_NAME; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/TreeLevel.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 33 | $this->fieldName = $fieldName; 34 | } 35 | 36 | /** 37 | * Enable TreeLevel. 38 | */ 39 | public static function enable() 40 | { 41 | Field::macro(self::MACRO_METHOD, function (Field $field) { 42 | return new static($field->getClassMetadata(), $field->getName()); 43 | }); 44 | } 45 | 46 | /** 47 | * Execute the build process. 48 | */ 49 | public function build() 50 | { 51 | if (!(new Validator())->isValidField($this->classMetadata, $this->fieldName)) { 52 | throw new InvalidMappingException("Tree level field must be 'integer' in class - {$this->classMetadata->name}"); 53 | } 54 | 55 | $this->classMetadata->mergeExtension($this->getExtensionName(), [ 56 | 'level' => $this->fieldName, 57 | ]); 58 | } 59 | 60 | /** 61 | * Return the name of the actual extension. 62 | * 63 | * @return string 64 | */ 65 | public function getExtensionName() 66 | { 67 | return FluentDriver::EXTENSION_NAME; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/TreePath.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 55 | $this->fieldName = $fieldName; 56 | $this->separator = $separator; 57 | } 58 | 59 | /** 60 | * Enable TreePath. 61 | */ 62 | public static function enable() 63 | { 64 | Field::macro(self::MACRO_METHOD, function (Field $field, $separator = '|') { 65 | return new static($field->getClassMetadata(), $field->getName(), $separator); 66 | }); 67 | } 68 | 69 | /** 70 | * @param string $separator 71 | * 72 | * @return TreePath 73 | */ 74 | public function separator($separator) 75 | { 76 | $this->separator = $separator; 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * @param bool $shouldIt 83 | * 84 | * @return TreePath 85 | */ 86 | public function appendId($shouldIt = true) 87 | { 88 | $this->appendId = $shouldIt; 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * @param bool $doesIt 95 | * 96 | * @return TreePath 97 | */ 98 | public function startsWithSeparator($doesIt = true) 99 | { 100 | $this->startsWithSeparator = $doesIt; 101 | 102 | return $this; 103 | } 104 | 105 | /** 106 | * @param bool $doesIt 107 | * 108 | * @return TreePath 109 | */ 110 | public function endsWithSeparator($doesIt = true) 111 | { 112 | $this->endsWithSeparator = $doesIt; 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * Execute the build process. 119 | */ 120 | public function build() 121 | { 122 | if (strlen($this->separator) > 1) { 123 | throw new InvalidMappingException("Tree Path field - [{$this->fieldName}] Separator {$this->separator} is invalid. It must be only one character long."); 124 | } 125 | 126 | $this->classMetadata->mergeExtension($this->getExtensionName(), [ 127 | 'path' => $this->fieldName, 128 | 'path_separator' => $this->separator, 129 | 'path_append_id' => $this->appendId, 130 | 'path_starts_with_separator' => $this->startsWithSeparator, 131 | 'path_ends_with_separator' => $this->endsWithSeparator, 132 | ]); 133 | } 134 | 135 | /** 136 | * Return the name of the actual extension. 137 | * 138 | * @return string 139 | */ 140 | protected function getExtensionName() 141 | { 142 | return FluentDriver::EXTENSION_NAME; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/TreePathHash.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 31 | $this->fieldName = $fieldName; 32 | } 33 | 34 | /** 35 | * Enable TreePathHash. 36 | */ 37 | public static function enable() 38 | { 39 | Field::macro(self::MACRO_METHOD, function (Field $field) { 40 | return new static($field->getClassMetadata(), $field->getName()); 41 | }); 42 | } 43 | 44 | /** 45 | * Execute the build process. 46 | */ 47 | public function build() 48 | { 49 | $this->classMetadata->mergeExtension($this->getExtensionName(), [ 50 | 'path_hash' => $this->fieldName, 51 | ]); 52 | } 53 | 54 | /** 55 | * Return the name of the actual extension. 56 | * 57 | * @return string 58 | */ 59 | public function getExtensionName() 60 | { 61 | return FluentDriver::EXTENSION_NAME; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/TreePathSource.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 33 | $this->fieldName = $fieldName; 34 | } 35 | 36 | /** 37 | * Enable TreePathSource. 38 | */ 39 | public static function enable() 40 | { 41 | Field::macro(self::MACRO_METHOD, function (Field $field) { 42 | return new static($field->getClassMetadata(), $field->getName()); 43 | }); 44 | } 45 | 46 | /** 47 | * Execute the build process. 48 | */ 49 | public function build() 50 | { 51 | if (!(new Validator())->isValidFieldForPathSource($this->classMetadata, $this->fieldName)) { 52 | throw new InvalidMappingException( 53 | "Tree PathSource field - [{$this->fieldName}] type is not valid. It can be any of the integer variants, double, float or string in class - {$this->classMetadata->name}" 54 | ); 55 | } 56 | 57 | $this->classMetadata->mergeExtension($this->getExtensionName(), [ 58 | 'path_source' => $this->fieldName, 59 | ]); 60 | } 61 | 62 | /** 63 | * Return the name of the actual extension. 64 | * 65 | * @return string 66 | */ 67 | public function getExtensionName() 68 | { 69 | return FluentDriver::EXTENSION_NAME; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/TreeRight.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 33 | $this->fieldName = $fieldName; 34 | } 35 | 36 | /** 37 | * Enable TreeRight. 38 | */ 39 | public static function enable() 40 | { 41 | Field::macro(self::MACRO_METHOD, function (Field $field) { 42 | return new static($field->getClassMetadata(), $field->getName()); 43 | }); 44 | } 45 | 46 | /** 47 | * Execute the build process. 48 | */ 49 | public function build() 50 | { 51 | if (!(new Validator())->isValidField($this->classMetadata, $this->fieldName)) { 52 | throw new InvalidMappingException("Tree right field must be 'integer' in class - {$this->classMetadata->name}"); 53 | } 54 | 55 | $this->classMetadata->mergeExtension($this->getExtensionName(), [ 56 | 'right' => $this->fieldName, 57 | ]); 58 | } 59 | 60 | /** 61 | * Return the name of the actual extension. 62 | * 63 | * @return string 64 | */ 65 | public function getExtensionName() 66 | { 67 | return FluentDriver::EXTENSION_NAME; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/TreeSelfReference.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 36 | $this->fieldName = $fieldName; 37 | $this->key = $key; 38 | } 39 | 40 | /** 41 | * Enable TreeRoot and TreeParent macros. 42 | * 43 | * @return void 44 | */ 45 | public static function enable() 46 | { 47 | static::enableRoot(); 48 | static::enableParent(); 49 | } 50 | 51 | /** 52 | * Enable only the TreeRoot macro. 53 | * 54 | * @return void 55 | */ 56 | public static function enableRoot() 57 | { 58 | static::addMacro('treeRoot', 'root'); 59 | } 60 | 61 | /** 62 | * Enable only the TreeParent macro. 63 | * 64 | * @return void 65 | */ 66 | public static function enableParent() 67 | { 68 | static::addMacro('treeParent', 'parent'); 69 | } 70 | 71 | /** 72 | * @param string $method 73 | * @param string $key 74 | * 75 | * @return void 76 | */ 77 | protected static function addMacro($method, $key) 78 | { 79 | Field::macro($method, function (Field $field) use ($key) { 80 | $field->nullable(); 81 | 82 | return new static($field->getClassMetadata(), $field->getName(), $key); 83 | }); 84 | 85 | ManyToOne::macro($method, function (ManyToOne $relation) use ($key) { 86 | $relation->nullable(); 87 | 88 | return new static($relation->getClassMetadata(), $relation->getRelation(), $key); 89 | }); 90 | } 91 | 92 | /** 93 | * Execute the build process. 94 | */ 95 | public function build() 96 | { 97 | $this->classMetadata->mergeExtension($this->getExtensionName(), [ 98 | $this->key => $this->fieldName, 99 | ]); 100 | } 101 | 102 | /** 103 | * Return the name of the actual extension. 104 | * 105 | * @return string 106 | */ 107 | public function getExtensionName() 108 | { 109 | return FluentDriver::EXTENSION_NAME; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/TreeStrategy.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 35 | } 36 | 37 | /** 38 | * Enable extension. 39 | */ 40 | public static function enable() 41 | { 42 | TreeLevel::enable(); 43 | TreeSelfReference::enableParent(); 44 | } 45 | 46 | /** 47 | * @param string $field 48 | * @param string $type 49 | * @param callable|null $callback 50 | * 51 | * @throws InvalidMappingException 52 | * 53 | * @return $this 54 | */ 55 | public function level($field = 'level', $type = 'integer', callable $callback = null) 56 | { 57 | $this->validateNumericField($type, $field); 58 | 59 | $this->mapField($type, $field, $callback); 60 | 61 | $this->level = $field; 62 | 63 | return $this; 64 | } 65 | 66 | /** 67 | * @param string $field 68 | * @param callable|null $callback 69 | * 70 | * @return $this 71 | */ 72 | public function parent($field = 'parent', callable $callback = null) 73 | { 74 | $this->addSelfReferencingRelation($field, $callback); 75 | 76 | $this->parent = $field; 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | */ 84 | public function build() 85 | { 86 | $this->defaults(); 87 | 88 | $this->getClassMetadata()->mergeExtension($this->getExtensionName(), $this->getValues()); 89 | } 90 | 91 | /** 92 | * Return the name of the actual extension. 93 | * 94 | * @return string 95 | */ 96 | protected function getExtensionName() 97 | { 98 | return FluentDriver::EXTENSION_NAME; 99 | } 100 | 101 | /** 102 | * @param string $type 103 | * @param string $field 104 | * @param callable|null $callback 105 | * @param bool|false $nullable 106 | */ 107 | protected function mapField($type, $field, callable $callback = null, $nullable = false) 108 | { 109 | $this->builder->field($type, $field, $callback)->nullable($nullable); 110 | } 111 | 112 | /** 113 | * @param string $type 114 | * @param string $field 115 | * 116 | * @throws InvalidMappingException 117 | */ 118 | protected function validateNumericField($type, $field) 119 | { 120 | if (!in_array($type, ['integer', 'bigint', 'smallint'])) { 121 | throw new InvalidMappingException("Invalid type [$type] for the [$field] field. Must be a (small, big) integer type."); 122 | } 123 | } 124 | 125 | /** 126 | * Returns the name of the mapped class. 127 | * 128 | * @return string 129 | */ 130 | protected function myself() 131 | { 132 | return $this->getClassMetadata()->name; 133 | } 134 | 135 | /** 136 | * @param string $field 137 | * @param callable|null $callback 138 | */ 139 | protected function addSelfReferencingRelation($field, callable $callback = null) 140 | { 141 | $this->builder->belongsTo($this->myself(), $field, $callback)->nullable(); 142 | } 143 | 144 | /** 145 | * @return ExtensibleClassMetadata 146 | */ 147 | protected function getClassMetadata() 148 | { 149 | return $this->builder->getBuilder()->getClassMetadata(); 150 | } 151 | 152 | /** 153 | * @return array 154 | */ 155 | protected function getValues() 156 | { 157 | $values = []; 158 | 159 | if ($this->parent) { 160 | $values['parent'] = $this->parent; 161 | } 162 | 163 | if ($this->level) { 164 | $values['level'] = $this->level; 165 | } 166 | 167 | return $values; 168 | } 169 | 170 | /** 171 | * Check if a given key is already configured for this extension. 172 | * 173 | * @param string $key 174 | * 175 | * @return bool 176 | */ 177 | protected function alreadyConfigured($key) 178 | { 179 | $config = $this->getClassMetadata()->getExtension($this->getExtensionName()); 180 | 181 | return isset($config[$key]); 182 | } 183 | 184 | /** 185 | * Check if parent is missing from both configuration and the current tree strategy builder. 186 | * 187 | * @return bool 188 | */ 189 | protected function isMissingParent() 190 | { 191 | return !$this->alreadyConfigured('parent') && !$this->parent; 192 | } 193 | 194 | /** 195 | * Set the default fields to fill if they were not configured. 196 | * All Tree strategies share one common required field: the parent reference. 197 | * 198 | * @return void 199 | */ 200 | protected function defaults() 201 | { 202 | if ($this->isMissingParent()) { 203 | $this->parent(); 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/UploadableFile.php: -------------------------------------------------------------------------------- 1 | validateType($type); 43 | 44 | $this->classMetadata = $classMetadata; 45 | $this->fieldName = $fieldName; 46 | $this->type = "file{$type}Field"; 47 | } 48 | 49 | /** 50 | * Enable the UploadableFile extension. 51 | * 52 | * @return void 53 | */ 54 | public static function enable() 55 | { 56 | foreach (self::$validTypes as $type) { 57 | Field::macro("asFile$type", function (Field $builder) use ($type) { 58 | return new static($builder->getClassMetadata(), $builder->getName(), $type); 59 | }); 60 | } 61 | } 62 | 63 | /** 64 | * Execute the build process. 65 | */ 66 | public function build() 67 | { 68 | $this->classMetadata->appendExtension(UploadableDriver::EXTENSION_NAME, [ 69 | $this->type => $this->fieldName, 70 | ]); 71 | } 72 | 73 | /** 74 | * Validate the given type of file field. 75 | * 76 | * @param string $type 77 | * 78 | * @return void 79 | */ 80 | private function validateType($type) 81 | { 82 | if (!in_array($type, self::$validTypes)) { 83 | throw new InvalidMappingException( 84 | 'Invalid uploadable field type reference. Must be one of: '.implode(', ', self::$validTypes) 85 | ); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Extensions/Gedmo/Versioned.php: -------------------------------------------------------------------------------- 1 | classMetadata = $classMetadata; 35 | $this->fieldName = $fieldName; 36 | } 37 | 38 | public static function enable() 39 | { 40 | Field::macro(self::MACRO_METHOD, function (Field $builder) { 41 | return new static($builder->getClassMetadata(), $builder->getName()); 42 | }); 43 | 44 | ManyToOne::macro(self::MACRO_METHOD, function (ManyToOne $builder) { 45 | return new static($builder->getClassMetadata(), $builder->getRelation()); 46 | }); 47 | 48 | OneToOne::macro(self::MACRO_METHOD, function (OneToOne $builder) { 49 | return new static($builder->getClassMetadata(), $builder->getRelation()); 50 | }); 51 | } 52 | 53 | /** 54 | * Execute the build process. 55 | */ 56 | public function build() 57 | { 58 | $config = $this->classMetadata->getExtension(Fluent::EXTENSION_NAME); 59 | 60 | $config['loggable'] = true; 61 | $config['versioned'] = array_unique(array_merge( 62 | isset($config['versioned']) ? $config['versioned'] : [], 63 | [ 64 | $this->fieldName, 65 | ] 66 | )); 67 | 68 | $this->classMetadata->addExtension(Fluent::EXTENSION_NAME, $config); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Extensions/GedmoExtensions.php: -------------------------------------------------------------------------------- 1 | addDriver( 88 | new FluentDriver($mappings), 89 | 'Gedmo' 90 | ); 91 | 92 | foreach (self::$extensions as $extension) { 93 | $extension::enable(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/FluentDriver.php: -------------------------------------------------------------------------------- 1 | fluentFactory = function (ClassMetadata $metadata) use ($namingStrategy) { 38 | return new Builder(new ClassMetadataBuilder($metadata), $namingStrategy); 39 | }; 40 | 41 | $this->mappers = new MapperSet(); 42 | $this->addMappings($mappings); 43 | } 44 | 45 | /** 46 | * Loads the metadata for the specified class into the provided container. 47 | * 48 | * @param string $className 49 | * @param ClassMetadata $metadata 50 | */ 51 | public function loadMetadataForClass($className, ClassMetadata $metadata) 52 | { 53 | $this->mappers->getMapperFor($className)->map( 54 | $this->getFluent($metadata) 55 | ); 56 | } 57 | 58 | /** 59 | * Gets the names of all mapped classes known to this driver. 60 | * 61 | * @throws MappingException 62 | * 63 | * @return string[] The names of all mapped classes known to this driver. 64 | */ 65 | public function getAllClassNames() 66 | { 67 | return $this->mappers->getClassNames(); 68 | } 69 | 70 | /** 71 | * Returns whether the class with the specified name should have its metadata loaded. 72 | * This is only the case if it is either mapped as an Entity or a MappedSuperclass. 73 | * 74 | * @param string $className 75 | * 76 | * @return bool 77 | */ 78 | public function isTransient($className) 79 | { 80 | return 81 | !$this->mappers->hasMapperFor($className) || 82 | $this->mappers->getMapperFor($className)->isTransient(); 83 | } 84 | 85 | /** 86 | * @param string[] $mappings 87 | */ 88 | public function addMappings(array $mappings = []) 89 | { 90 | foreach ($mappings as $class) { 91 | if (!class_exists($class)) { 92 | throw new InvalidArgumentException("Mapping class [{$class}] does not exist"); 93 | } 94 | 95 | $mapping = new $class(); 96 | 97 | if (!$mapping instanceof Mapping) { 98 | throw new InvalidArgumentException("Mapping class [{$class}] should implement ".Mapping::class); 99 | } 100 | 101 | $this->addMapping($mapping); 102 | } 103 | } 104 | 105 | /** 106 | * @param Mapping $mapping 107 | * 108 | * @throws MappingException 109 | * 110 | * @return void 111 | */ 112 | public function addMapping(Mapping $mapping) 113 | { 114 | $this->mappers->add($mapping); 115 | } 116 | 117 | /** 118 | * @return MapperSet 119 | */ 120 | public function getMappers() 121 | { 122 | return $this->mappers; 123 | } 124 | 125 | /** 126 | * Override the default Fluent factory method with a custom one. 127 | * Use this to implement your own Fluent builder. 128 | * The method will receive a ClassMetadata object as its only argument. 129 | * 130 | * @param callable $factory 131 | */ 132 | public function setFluentFactory(callable $factory) 133 | { 134 | $this->fluentFactory = $factory; 135 | } 136 | 137 | /** 138 | * @param ClassMetadata $metadata 139 | * 140 | * @return Fluent 141 | */ 142 | protected function getFluent(ClassMetadata $metadata) 143 | { 144 | return call_user_func($this->fluentFactory, $metadata); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/MappedSuperClassMapping.php: -------------------------------------------------------------------------------- 1 | addMapper($this->mapFor(), new MappedSuperClassMapper($this)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Mappers/AbstractMapper.php: -------------------------------------------------------------------------------- 1 | mapping = $mapping; 22 | } 23 | 24 | /** 25 | * @param Fluent $builder 26 | */ 27 | public function map(Fluent $builder) 28 | { 29 | $this->setType($builder->getBuilder()); 30 | 31 | $this->mapping->map($builder); 32 | 33 | $builder->build(); 34 | } 35 | 36 | /** 37 | * @param ClassMetadataBuilder $metadata 38 | */ 39 | protected function setType(ClassMetadataBuilder $metadata) 40 | { 41 | // By default nothing has to be done 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Mappers/EmbeddableMapper.php: -------------------------------------------------------------------------------- 1 | setEmbeddable(); 15 | } 16 | 17 | /** 18 | * Returns whether the class with the specified name should have its metadata loaded. 19 | * This is only the case if it is either mapped as an Entity or a MappedSuperclass. 20 | * 21 | * @return bool 22 | */ 23 | public function isTransient() 24 | { 25 | return true; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Mappers/EntityMapper.php: -------------------------------------------------------------------------------- 1 | setMappedSuperClass(); 15 | } 16 | 17 | /** 18 | * Returns whether the class with the specified name should have its metadata loaded. 19 | * This is only the case if it is either mapped as an Entity or a MappedSuperclass. 20 | * 21 | * @return bool 22 | */ 23 | public function isTransient() 24 | { 25 | return false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Mappers/Mapper.php: -------------------------------------------------------------------------------- 1 | addMapperTo($this); 26 | } 27 | 28 | /** 29 | * @param string $className 30 | * 31 | * @throws MappingException 32 | * 33 | * @return Mapper 34 | */ 35 | public function getMapperFor($className) 36 | { 37 | if (!$this->hasMapperFor($className)) { 38 | throw new MappingException(sprintf( 39 | 'Class [%s] does not have a mapping configuration. '. 40 | 'Make sure you create a Mapping class that extends either %s, %s or %s. '. 41 | 'If you are using inheritance mapping, remember to create mappings '. 42 | 'for every child of the inheritance tree.', 43 | $className, 44 | EntityMapping::class, 45 | EmbeddableMapping::class, 46 | MappedSuperClassMapping::class 47 | )); 48 | } 49 | 50 | return $this->mappers[$className]; 51 | } 52 | 53 | /** 54 | * @param string $className 55 | * 56 | * @return bool 57 | */ 58 | public function hasMapperFor($className) 59 | { 60 | return isset($this->mappers[$className]); 61 | } 62 | 63 | /** 64 | * @return bool 65 | */ 66 | public function hasMappers() 67 | { 68 | return count($this->mappers) > 0; 69 | } 70 | 71 | /** 72 | * @return Mapper[] 73 | */ 74 | public function getMappers() 75 | { 76 | return $this->mappers; 77 | } 78 | 79 | /** 80 | * @return string[] 81 | */ 82 | public function getClassNames() 83 | { 84 | return array_keys($this->mappers); 85 | } 86 | 87 | /** 88 | * Add a mapper to the given class. 89 | * 90 | * @param string $class 91 | * @param Mapper $mapper 92 | */ 93 | public function addMapper($class, Mapper $mapper) 94 | { 95 | $this->mappers[$class] = $mapper; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Mapping.php: -------------------------------------------------------------------------------- 1 | entity = $entity; 66 | $this->builder = $builder; 67 | $this->relation = $relation; 68 | $this->namingStrategy = $namingStrategy; 69 | $this->association = $this->createAssociation($builder, $relation, $entity); 70 | } 71 | 72 | /** 73 | * @param ClassMetadataBuilder $builder 74 | * @param string $relation 75 | * @param string $entity 76 | * 77 | * @return AssociationBuilder 78 | */ 79 | abstract protected function createAssociation(ClassMetadataBuilder $builder, $relation, $entity); 80 | 81 | /** 82 | * @param string[] $cascade one of "persist", "remove", "merge", "detach", "refresh" or "ALL" 83 | * 84 | * @return $this 85 | */ 86 | public function cascade(array $cascade) 87 | { 88 | foreach ($cascade as $name) { 89 | $method = 'cascade'.(InflectorFactory::create()->build())->classify(strtolower($name)); 90 | 91 | if (!method_exists($this->association, $method)) { 92 | throw new InvalidArgumentException('Cascade ['.$name.'] does not exist'); 93 | } 94 | 95 | $this->{$method}(); 96 | } 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * @param string $strategy one of "LAZY", "EAGER", "EXTRA_LAZY" 103 | * 104 | * @return $this 105 | */ 106 | public function fetch($strategy) 107 | { 108 | $method = 'fetch'.(InflectorFactory::create()->build())->classify(strtolower($strategy)); 109 | 110 | if (!method_exists($this->association, $method)) { 111 | throw new InvalidArgumentException('Fetch ['.$strategy.'] does not exist'); 112 | } 113 | 114 | $this->{$method}(); 115 | 116 | return $this; 117 | } 118 | 119 | /** 120 | * @return ClassMetadataBuilder 121 | */ 122 | public function getBuilder() 123 | { 124 | return $this->builder; 125 | } 126 | 127 | /** 128 | * @return \Doctrine\ORM\Mapping\ClassMetadata|ExtensibleClassMetadata 129 | */ 130 | public function getClassMetadata() 131 | { 132 | return $this->builder->getClassMetadata(); 133 | } 134 | 135 | /** 136 | * @return AssociationBuilder 137 | */ 138 | public function getAssociation() 139 | { 140 | return $this->association; 141 | } 142 | 143 | /** 144 | * @param string $usage 145 | * @param string|null $region 146 | * 147 | * @return $this 148 | */ 149 | public function cache($usage = 'READ_ONLY', $region = null) 150 | { 151 | $cache = new AssociationCache( 152 | $this->builder->getClassMetadata(), 153 | $this->relation, 154 | $usage, 155 | $region 156 | ); 157 | 158 | $this->queue($cache); 159 | 160 | return $this; 161 | } 162 | 163 | /** 164 | * Execute the build process for all queued buildables. 165 | */ 166 | public function build() 167 | { 168 | $this->getAssociation()->build(); 169 | 170 | $this->buildQueued(); 171 | } 172 | 173 | /** 174 | * Magic call method works as a proxy for the Doctrine associationBuilder. 175 | * 176 | * @param string $method 177 | * @param array $args 178 | * 179 | * @throws BadMethodCallException 180 | * 181 | * @return $this 182 | */ 183 | public function __call($method, $args) 184 | { 185 | if (method_exists($this->getAssociation(), $method)) { 186 | call_user_func_array([$this->getAssociation(), $method], $args); 187 | 188 | return $this; 189 | } 190 | 191 | throw new BadMethodCallException("Relation method [{$method}] does not exist."); 192 | } 193 | 194 | /** 195 | * @return NamingStrategy 196 | */ 197 | public function getNamingStrategy() 198 | { 199 | return $this->namingStrategy; 200 | } 201 | 202 | /** 203 | * @return string 204 | */ 205 | public function getRelation() 206 | { 207 | return $this->relation; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/Relations/AssociationCache.php: -------------------------------------------------------------------------------- 1 | ClassMetadataInfo::CACHE_USAGE_READ_ONLY, 27 | 'NONSTRICT_READ_WRITE' => ClassMetadataInfo::CACHE_USAGE_NONSTRICT_READ_WRITE, 28 | 'READ_WRITE' => ClassMetadataInfo::CACHE_USAGE_READ_WRITE, 29 | ]; 30 | 31 | /** 32 | * @var string 33 | */ 34 | protected $field; 35 | 36 | /** 37 | * @var ClassMetadata 38 | */ 39 | protected $metadata; 40 | 41 | /** 42 | * @param ClassMetadata $metadata 43 | * @param string $field 44 | * @param string|int $usage 45 | * @param string|null $region 46 | */ 47 | public function __construct(ClassMetadata $metadata, $field, $usage = 'READ_ONLY', $region = null) 48 | { 49 | $this->field = $field; 50 | $this->metadata = $metadata; 51 | $this->setRegion($region); 52 | $this->setUsage($usage); 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getUsage() 59 | { 60 | return $this->usage; 61 | } 62 | 63 | /** 64 | * @param string $usage 65 | * 66 | * @throws InvalidArgumentException 67 | * 68 | * @return AssociationCache 69 | */ 70 | public function setUsage($usage) 71 | { 72 | if (is_int($usage)) { 73 | $this->validate($usage, $this->usages); 74 | } else { 75 | $this->validate($usage, array_keys($this->usages)); 76 | $usage = $this->usages[$usage]; 77 | } 78 | 79 | $this->usage = $usage; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * @return string 86 | */ 87 | public function getRegion() 88 | { 89 | return $this->region; 90 | } 91 | 92 | /** 93 | * @param string $region 94 | * 95 | * @return AssociationCache 96 | */ 97 | public function setRegion($region) 98 | { 99 | $this->region = $region; 100 | 101 | return $this; 102 | } 103 | 104 | /** 105 | * Execute the build process. 106 | */ 107 | public function build() 108 | { 109 | $this->metadata->enableAssociationCache($this->field, [ 110 | 'usage' => $this->getUsage(), 111 | 'region' => $this->getRegion(), 112 | ]); 113 | } 114 | 115 | /** 116 | * @param string|int $usage 117 | * @param array $usages 118 | * 119 | * @return mixed 120 | */ 121 | protected function validate($usage, array $usages) 122 | { 123 | if (!in_array($usage, $usages)) { 124 | throw new InvalidArgumentException( 125 | '['.$usage.'] is not a valid cache usage. Available: '.implode(', ', array_keys($this->usages)) 126 | ); 127 | } 128 | 129 | return $usage; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/Relations/JoinColumn.php: -------------------------------------------------------------------------------- 1 | namingStrategy = $namingStrategy; 70 | $this->relation = $relation; 71 | $this->joinColumn = $joinColumn; 72 | $this->referenceColumn = $referenceColumn; 73 | $this->nullable = $nullable; 74 | $this->unique = $unique; 75 | $this->onDelete = $onDelete; 76 | $this->columnDefinition = $columnDefinition; 77 | } 78 | 79 | /** 80 | * @param string $foreignKey 81 | * 82 | * @return $this 83 | */ 84 | public function foreignKey($foreignKey) 85 | { 86 | $this->joinColumn = $foreignKey; 87 | 88 | return $this; 89 | } 90 | 91 | /** 92 | * @param string $foreignKey 93 | * 94 | * @return $this 95 | */ 96 | public function target($foreignKey) 97 | { 98 | return $this->foreignKey($foreignKey); 99 | } 100 | 101 | /** 102 | * @param string $localKey 103 | * 104 | * @return $this 105 | */ 106 | public function localKey($localKey) 107 | { 108 | $this->referenceColumn = $localKey; 109 | 110 | return $this; 111 | } 112 | 113 | /** 114 | * @param string $localKey 115 | * 116 | * @return $this 117 | */ 118 | public function source($localKey) 119 | { 120 | return $this->localKey($localKey); 121 | } 122 | 123 | /** 124 | * @return string 125 | */ 126 | public function getJoinColumn() 127 | { 128 | return $this->joinColumn ?: $this->namingStrategy->joinColumnName($this->relation); 129 | } 130 | 131 | /** 132 | * @param string $joinColumn 133 | * 134 | * @return $this 135 | */ 136 | public function setJoinColumn($joinColumn) 137 | { 138 | $this->joinColumn = $joinColumn; 139 | 140 | return $this; 141 | } 142 | 143 | /** 144 | * @return string 145 | */ 146 | public function getReferenceColumn() 147 | { 148 | return $this->referenceColumn ?: $this->namingStrategy->referenceColumnName(); 149 | } 150 | 151 | /** 152 | * @param string $referenceColumn 153 | * 154 | * @return $this 155 | */ 156 | public function setReferenceColumn($referenceColumn) 157 | { 158 | $this->referenceColumn = $referenceColumn; 159 | 160 | return $this; 161 | } 162 | 163 | /** 164 | * @return $this 165 | */ 166 | public function nullable() 167 | { 168 | $this->nullable = true; 169 | 170 | return $this; 171 | } 172 | 173 | /** 174 | * @return bool 175 | */ 176 | public function isNullable() 177 | { 178 | return $this->nullable; 179 | } 180 | 181 | /** 182 | * @return bool 183 | */ 184 | public function isUnique() 185 | { 186 | return $this->unique; 187 | } 188 | 189 | /** 190 | * @return $this 191 | */ 192 | public function unique() 193 | { 194 | $this->unique = true; 195 | 196 | return $this; 197 | } 198 | 199 | /** 200 | * @return string|null 201 | */ 202 | public function getOnDelete() 203 | { 204 | return $this->onDelete; 205 | } 206 | 207 | /** 208 | * @param string|null $onDelete 209 | * 210 | * @return $this 211 | */ 212 | public function onDelete($onDelete = null) 213 | { 214 | $this->onDelete = $onDelete; 215 | 216 | return $this; 217 | } 218 | 219 | /** 220 | * @return string|null 221 | */ 222 | public function getColumnDefinition() 223 | { 224 | return $this->columnDefinition; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/Relations/ManyToMany.php: -------------------------------------------------------------------------------- 1 | builder->createManyToMany($relation, $entity); 48 | } 49 | 50 | /** 51 | * @param string $table 52 | * 53 | * @return $this 54 | */ 55 | public function joinTable($table) 56 | { 57 | $this->association->setJoinTable($table); 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * @param string $joinColumn 64 | * @param string $references 65 | * @param bool $unique 66 | * 67 | * @return $this 68 | */ 69 | public function joinColumn($joinColumn, $references = 'id', $unique = false) 70 | { 71 | $this->addJoinColumn(null, $joinColumn, $references, false, $unique); 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * @param string $foreignKey 78 | * @param string $references 79 | * @param bool $unique 80 | * 81 | * @return $this 82 | */ 83 | public function foreignKey($foreignKey, $references = 'id', $unique = false) 84 | { 85 | return $this->joinColumn($foreignKey, $references, $unique); 86 | } 87 | 88 | /** 89 | * @param string $foreignKey 90 | * @param string $references 91 | * @param bool $unique 92 | * 93 | * @return $this 94 | */ 95 | public function source($foreignKey, $references = 'id', $unique = false) 96 | { 97 | return $this->joinColumn($foreignKey, $references, $unique); 98 | } 99 | 100 | /** 101 | * @param string $inverseKey 102 | * @param string $references 103 | * @param bool $unique 104 | * 105 | * @return $this 106 | */ 107 | public function inverseKey($inverseKey, $references = 'id', $unique = false) 108 | { 109 | $this->association->addInverseJoinColumn($inverseKey, $references, false, $unique); 110 | 111 | return $this; 112 | } 113 | 114 | /** 115 | * @param string $inverseKey 116 | * @param string $references 117 | * @param bool $unique 118 | * 119 | * @return $this 120 | */ 121 | public function target($inverseKey, $references = 'id', $unique = false) 122 | { 123 | return $this->inverseKey($inverseKey, $references, $unique); 124 | } 125 | 126 | /** 127 | * Magic call method works as a proxy for the Doctrine associationBuilder. 128 | * 129 | * @param string $method 130 | * @param array $args 131 | * 132 | * @throws \BadMethodCallException 133 | * 134 | * @return $this 135 | */ 136 | public function __call($method, $args) 137 | { 138 | if ($this->hasMacro($method)) { 139 | return $this->queueMacro($method, $args); 140 | } 141 | 142 | return parent::__call($method, $args); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Relations/ManyToOne.php: -------------------------------------------------------------------------------- 1 | addJoinColumn($relation); 47 | } 48 | 49 | /** 50 | * @param ClassMetadataBuilder $builder 51 | * @param string $relation 52 | * @param string $entity 53 | * 54 | * @return AssociationBuilder 55 | */ 56 | protected function createAssociation(ClassMetadataBuilder $builder, $relation, $entity) 57 | { 58 | return $this->builder->createManyToOne( 59 | $relation, 60 | $entity 61 | ); 62 | } 63 | 64 | /** 65 | * @param callable|null $callback 66 | * 67 | * @return JoinColumn|false 68 | */ 69 | public function getJoinColumn(callable $callback = null) 70 | { 71 | $joinColumn = reset($this->joinColumns); 72 | 73 | if (is_callable($callback)) { 74 | $callback($joinColumn); 75 | } 76 | 77 | return $joinColumn; 78 | } 79 | 80 | /** 81 | * Magic call method works as a proxy for the Doctrine associationBuilder. 82 | * 83 | * @param string $method 84 | * @param array $args 85 | * 86 | * @throws \BadMethodCallException 87 | * 88 | * @return $this 89 | */ 90 | public function __call($method, $args) 91 | { 92 | if ($this->hasMacro($method)) { 93 | return $this->queueMacro($method, $args); 94 | } 95 | 96 | if (method_exists($this->getJoinColumn(), $method)) { 97 | call_user_func_array([$this->getJoinColumn(), $method], $args); 98 | 99 | return $this; 100 | } 101 | 102 | return parent::__call($method, $args); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Relations/OneToMany.php: -------------------------------------------------------------------------------- 1 | builder->createOneToMany($relation, $entity); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Relations/OneToOne.php: -------------------------------------------------------------------------------- 1 | builder->createOneToOne( 35 | $relation, 36 | $entity 37 | ); 38 | } 39 | 40 | /** 41 | * @param string $method 42 | * @param array $args 43 | * 44 | * @throws \BadMethodCallException 45 | * 46 | * @return $this 47 | */ 48 | public function __call($method, $args) 49 | { 50 | if ($this->hasMacro($method)) { 51 | return $this->queueMacro($method, $args); 52 | } 53 | 54 | return parent::__call($method, $args); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Relations/Relation.php: -------------------------------------------------------------------------------- 1 | getAssociation()->setIndexBy($index); 15 | 16 | return $this; 17 | } 18 | 19 | /** 20 | * @return \Doctrine\ORM\Mapping\Builder\OneToManyAssociationBuilder 21 | */ 22 | abstract public function getAssociation(); 23 | } 24 | -------------------------------------------------------------------------------- /src/Relations/Traits/ManyTo.php: -------------------------------------------------------------------------------- 1 | getJoinColumns() as $column) { 20 | $this->getAssociation()->addJoinColumn( 21 | $column->getJoinColumn(), 22 | $column->getReferenceColumn(), 23 | $column->isNullable(), 24 | $column->isUnique(), 25 | $column->getOnDelete(), 26 | $column->getColumnDefinition() 27 | ); 28 | } 29 | 30 | parent::build(); 31 | } 32 | 33 | /** 34 | * @param string $relation 35 | * @param string|null $joinColumn 36 | * @param string|null $referenceColumn 37 | * @param bool|false $nullable 38 | * @param bool|false $unique 39 | * @param string|null $onDelete 40 | * @param string|null $columnDefinition 41 | * 42 | * @return $this 43 | */ 44 | public function addJoinColumn( 45 | $relation, 46 | $joinColumn = null, 47 | $referenceColumn = null, 48 | $nullable = false, 49 | $unique = false, 50 | $onDelete = null, 51 | $columnDefinition = null 52 | ) { 53 | $joinColumn = new JoinColumn( 54 | $this->getNamingStrategy(), 55 | $relation, 56 | $joinColumn, 57 | $referenceColumn, 58 | $nullable, 59 | $unique, 60 | $onDelete, 61 | $columnDefinition 62 | ); 63 | 64 | $this->pushJoinColumn($joinColumn); 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * @param JoinColumn $column 71 | */ 72 | protected function pushJoinColumn(JoinColumn $column) 73 | { 74 | $this->joinColumns[] = $column; 75 | } 76 | 77 | /** 78 | * @return JoinColumn[] 79 | */ 80 | public function getJoinColumns() 81 | { 82 | return $this->joinColumns; 83 | } 84 | 85 | /** 86 | * @return \Doctrine\ORM\Mapping\Builder\AssociationBuilder 87 | */ 88 | abstract public function getAssociation(); 89 | 90 | /** 91 | * @return \Doctrine\ORM\Mapping\NamingStrategy 92 | */ 93 | abstract public function getNamingStrategy(); 94 | } 95 | -------------------------------------------------------------------------------- /src/Relations/Traits/Orderable.php: -------------------------------------------------------------------------------- 1 | getAssociation()->setOrderBy([ 16 | $name => $order, 17 | ]); 18 | 19 | return $this; 20 | } 21 | 22 | /** 23 | * @return \Doctrine\ORM\Mapping\Builder\OneToManyAssociationBuilder 24 | */ 25 | abstract public function getAssociation(); 26 | } 27 | -------------------------------------------------------------------------------- /src/Relations/Traits/Ownable.php: -------------------------------------------------------------------------------- 1 | getAssociation()->mappedBy($relation); 15 | 16 | return $this; 17 | } 18 | 19 | /** 20 | * @return \Doctrine\ORM\Mapping\Builder\AssociationBuilder 21 | */ 22 | abstract public function getAssociation(); 23 | } 24 | -------------------------------------------------------------------------------- /src/Relations/Traits/Owning.php: -------------------------------------------------------------------------------- 1 | getAssociation()->inversedBy($relation); 15 | 16 | return $this; 17 | } 18 | 19 | /** 20 | * @return \Doctrine\ORM\Mapping\Builder\AssociationBuilder 21 | */ 22 | abstract public function getAssociation(); 23 | } 24 | -------------------------------------------------------------------------------- /src/Relations/Traits/Primary.php: -------------------------------------------------------------------------------- 1 | getAssociation()->makePrimaryKey(); 16 | 17 | return $this; 18 | } 19 | } 20 | --------------------------------------------------------------------------------