diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be2df9b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/.idea + +/.php-cs-fixer.php +/.php-cs-fixer.cache + +/vendor +/composer.lock diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..1f087eb --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,66 @@ +in(__DIR__); + +return (new PhpCsFixer\Config()) + ->setFinder($finder) + ->setRules([ + '@PSR12' => true, + 'blank_line_after_opening_tag' => true, + 'braces' => [ + 'allow_single_line_anonymous_class_with_empty_body' => true, + ], + 'compact_nullable_typehint' => true, + 'declare_equal_normalize' => true, + 'lowercase_cast' => true, + 'lowercase_static_reference' => true, + 'new_with_braces' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_leading_import_slash' => true, + 'no_whitespace_in_blank_line' => true, + 'ordered_class_elements' => [ + 'order' => [ + 'use_trait', + ], + ], + 'ordered_imports' => [ + 'imports_order' => [ + 'class', + 'function', + 'const', + ], + 'sort_algorithm' => 'alpha', + ], + 'return_type_declaration' => true, + 'short_scalar_cast' => true, + 'single_trait_insert_per_statement' => true, + 'ternary_operator_spaces' => true, + 'visibility_required' => [ + 'elements' => [ + 'const', + 'method', + 'property', + ], + ], + 'array_syntax' => [ + 'syntax' => 'short', + ], + 'concat_space' => [ + 'spacing' => 'one', + ], + 'fully_qualified_strict_types' => true, + 'native_function_invocation' => [ + 'include' => [], + 'strict' => true, + ], + 'no_unused_imports' => true, + 'single_quote' => true, + 'space_after_semicolon' => true, + 'trailing_comma_in_multiline' => true, + 'trim_array_spaces' => true, + 'unary_operator_spaces' => true, + 'whitespace_after_comma_in_array' => true, + ]) + ->setRiskyAllowed(true); diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0d9c541 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Sergey Zakharevich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ff7bccf --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# IDE Helper Generator for October CMS + +**Complete PHPDocs, directly from the source** + +This package generates helper files that enable your IDE to provide accurate autocompletion. +Generation is done based on the files in your project, so they are always up-to-date. +This package extends the [Laravel IDE Helper package](https://github.com/barryvdh/laravel-ide-helper) + +- [Installation](#installation) +- [Usage](#usage) +- [License](#license) + +## Installation + +Require this package with composer using the following command: + +```bash +composer require --dev wobqqq/oc-ide-helper +``` + +Add the following class to the `providers` array in `config/app.php`: + +```php +'providers' => array_merge(include(base_path('modules/system/providers.php')), [ + Wobqqq\IdeHelper\IdeHelperServiceProvider::class, +]), +``` + +Publish the `config/ide-helper.php` configuration file: + +```bash +php artisan vendor:publish --provider="Wobqqq\IdeHelper\IdeHelperServiceProvider" --tag=config +``` + +If you want, you can change the path to the models in the `config/ide-helper.php` config file: + +```php +'model_locations' => [ + './plugins/MyPlugins/MyPlugin/models/' +], +``` + +## Usage + +Basic commands: + +- PHPDoc generation for Laravel Facades - `php artisan ide-helper:generate` +- PHPDocs for models - `php artisan ide-helper:models` +- PhpStorm Meta file - `php artisan ide-helper:meta` + +You can get more information on usage [here](https://github.com/barryvdh/laravel-ide-helper?tab=readme-ov-file#usage). + +## License + +The Laravel IDE Helper Generator is open-sourced software licensed under +the [MIT license](http://opensource.org/licenses/MIT) diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..61389fc --- /dev/null +++ b/composer.json @@ -0,0 +1,41 @@ +{ + "name": "wobqqq/oc-ide-helper", + "description": "October CMS IDE Helper (extended laravel-ide-helper package)", + "homepage": "https://github.com/wobqqq/oc-ide-helper", + "keywords": [ + "OctoberCms", + "PHP", + "IDE" + ], + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Sergey Zakharevich", + "email": "wobqqq@gmail.com" + } + ], + "require": { + "php": "^7.1 || ^8.0", + "barryvdh/laravel-ide-helper": "^2.5.0" + }, + "require-dev": { + "illuminate/database": "^10.38 || ^11", + "october/rain": "^3.6", + "friendsofphp/php-cs-fixer": "^3", + "phpstan/phpstan": "^1.10" + }, + "autoload": { + "psr-4": { + "Wobqqq\\IdeHelper\\": "src/" + } + }, + "scripts": { + "code.cs-fixer": "php-cs-fixer --diff fix", + "code.phpstan": "vendor/bin/phpstan analyse --memory-limit=512M", + "code.fix_and_analyse": [ + "@code.cs-fixer", + "@code.phpstan" + ] + } +} diff --git a/config/ide-helper.php b/config/ide-helper.php new file mode 100644 index 0000000..8bb2bfd --- /dev/null +++ b/config/ide-helper.php @@ -0,0 +1,347 @@ + '_ide_helper.php', + + /* + |-------------------------------------------------------------------------- + | Models filename + |-------------------------------------------------------------------------- + | + | The default filename for the models helper file + | + */ + + 'models_filename' => '_ide_helper_models.php', + + /* + |-------------------------------------------------------------------------- + | Where to write the PhpStorm specific meta file + |-------------------------------------------------------------------------- + | + | PhpStorm also supports the directory `.phpstorm.meta.php/` with arbitrary + | files in it, should you need additional files for your project; e.g. + | `.phpstorm.meta.php/laravel_ide_Helper.php'. + | + */ + 'meta_filename' => '.phpstorm.meta.php', + + /* + |-------------------------------------------------------------------------- + | Fluent helpers + |-------------------------------------------------------------------------- + | + | Set to true to generate commonly used Fluent methods + | + */ + + 'include_fluent' => false, + + /* + |-------------------------------------------------------------------------- + | Factory Builders + |-------------------------------------------------------------------------- + | + | Set to true to generate factory generators for better factory() + | method auto-completion. + | + | Deprecated for Laravel 8 or latest. + | + */ + + 'include_factory_builders' => false, + + /* + |-------------------------------------------------------------------------- + | Write Model Magic methods + |-------------------------------------------------------------------------- + | + | Set to false to disable write magic methods of model + | + */ + + 'write_model_magic_where' => true, + + /* + |-------------------------------------------------------------------------- + | Write Model External Eloquent Builder methods + |-------------------------------------------------------------------------- + | + | Set to false to disable write external eloquent builder methods + | + */ + + 'write_model_external_builder_methods' => true, + + /* + |-------------------------------------------------------------------------- + | Write Model relation count properties + |-------------------------------------------------------------------------- + | + | Set to false to disable writing of relation count properties to model DocBlocks. + | + */ + + 'write_model_relation_count_properties' => true, + + /* + |-------------------------------------------------------------------------- + | Write Eloquent Model Mixins + |-------------------------------------------------------------------------- + | + | This will add the necessary DocBlock mixins to the model class + | contained in the Laravel Framework. This helps the IDE with + | auto-completion. + | + | Please be aware that this setting changes a file within the /vendor directory. + | + */ + + 'write_eloquent_model_mixins' => false, + + /* + |-------------------------------------------------------------------------- + | Helper files to include + |-------------------------------------------------------------------------- + | + | Include helper files. By default not included, but can be toggled with the + | -- helpers (-H) option. Extra helper files can be included. + | + */ + + 'include_helpers' => false, + + 'helper_files' => [ + base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php', + base_path() . '/vendor/october/rain/src/Support/helpers.php', + ], + + /* + |-------------------------------------------------------------------------- + | Model locations to include + |-------------------------------------------------------------------------- + | + | Define in which directories the ide-helper:models command should look + | for models. + | + | glob patterns are supported to easier reach models in sub-directories, + | e.g. `app/Services/* /Models` (without the space) + | + */ + + 'model_locations' => [ + './plugins/*/*/models/', + ], + + /* + |-------------------------------------------------------------------------- + | Models to ignore + |-------------------------------------------------------------------------- + | + | Define which models should be ignored. + | + */ + + 'ignored_models' => [ + + ], + + /* + |-------------------------------------------------------------------------- + | Models hooks + |-------------------------------------------------------------------------- + | + | Define which hook classes you want to run for models to add custom information + | + | Hooks should implement Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface. + | + */ + + 'model_hooks' => [ + Wobqqq\IdeHelper\Hooks\ModelHook::class, + ], + + /* + |-------------------------------------------------------------------------- + | Extra classes + |-------------------------------------------------------------------------- + | + | These implementations are not really extended, but called with magic functions + | + */ + + 'extra' => [ + 'Eloquent' => ['October\Rain\Database\Builder', 'October\Rain\Database\QueryBuilder'], + 'Session' => ['Illuminate\Session\Store'], + ], + + 'magic' => [], + + /* + |-------------------------------------------------------------------------- + | Interface implementations + |-------------------------------------------------------------------------- + | + | These interfaces will be replaced with the implementing class. Some interfaces + | are detected by the helpers, others can be listed below. + | + */ + + 'interfaces' => [ + + ], + + /* + |-------------------------------------------------------------------------- + | Support for custom DB types + |-------------------------------------------------------------------------- + | + | This setting allow you to map any custom database type (that you may have + | created using CREATE TYPE statement or imported using database plugin + | / extension to a Doctrine type. + | + | Each key in this array is a name of the Doctrine2 DBAL Platform. Currently valid names are: + | 'postgresql', 'db2', 'drizzle', 'mysql', 'oracle', 'sqlanywhere', 'sqlite', 'mssql' + | + | This name is returned by getName() method of the specific Doctrine/DBAL/Platforms/AbstractPlatform descendant + | + | The value of the array is an array of type mappings. Key is the name of the custom type, + | (for example, "jsonb" from Postgres 9.4) and the value is the name of the corresponding Doctrine2 type (in + | our case it is 'json_array'. Doctrine types are listed here: + | https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html#types + | + | So to support jsonb in your models when working with Postgres, just add the following entry to the array below: + | + | "postgresql" => array( + | "jsonb" => "json_array", + | ), + | + */ + 'custom_db_types' => [ + + ], + + /* + |-------------------------------------------------------------------------- + | Support for camel cased models + |-------------------------------------------------------------------------- + | + | There are some Laravel packages (such as Eloquence) that allow for accessing + | Eloquent model properties via camel case, instead of snake case. + | + | Enabling this option will support these packages by saving all model + | properties as camel case, instead of snake case. + | + | For example, normally you would see this: + | + | * @property \Illuminate\Support\Carbon $created_at + | * @property \Illuminate\Support\Carbon $updated_at + | + | With this enabled, the properties will be this: + | + | * @property \Illuminate\Support\Carbon $createdAt + | * @property \Illuminate\Support\Carbon $updatedAt + | + | Note, it is currently an all-or-nothing option. + | + */ + 'model_camel_case_properties' => false, + + /* + |-------------------------------------------------------------------------- + | Property Casts + |-------------------------------------------------------------------------- + | + | Cast the given "real type" to the given "type". + | + */ + 'type_overrides' => [ + 'integer' => 'int', + 'boolean' => 'bool', + ], + + /* + |-------------------------------------------------------------------------- + | Include DocBlocks from classes + |-------------------------------------------------------------------------- + | + | Include DocBlocks from classes to allow additional code inspection for + | magic methods and properties. + | + */ + 'include_class_docblocks' => false, + + /* + |-------------------------------------------------------------------------- + | Force FQN usage + |-------------------------------------------------------------------------- + | + | Use the fully qualified (class) name in docBlock, + | event if class exists in a given file + | or there is an import (use className) of a given class + | + */ + 'force_fqn' => false, + + /* + |-------------------------------------------------------------------------- + | Use generics syntax + |-------------------------------------------------------------------------- + | + | Use generics syntax within DocBlocks, + | e.g. `Collection` instead of `Collection|User[]`. + | + */ + 'use_generics_annotations' => true, + + /* + |-------------------------------------------------------------------------- + | Additional relation types + |-------------------------------------------------------------------------- + | + | Sometimes it's needed to create custom relation types. The key of the array + | is the Relationship Method name. The value of the array is the canonical class + | name of the Relationship, e.g. `'relationName' => RelationShipClass::class`. + | + */ + 'additional_relation_types' => [], + + /* + |-------------------------------------------------------------------------- + | Additional relation return types + |-------------------------------------------------------------------------- + | + | When using custom relation types its possible for the class name to not contain + | the proper return type of the relation. The key of the array is the relationship + | method name. The value of the array is the return type of the relation ('many' + | or 'morphTo'). + | e.g. `'relationName' => 'many'`. + | + */ + 'additional_relation_return_types' => [], + + /* + |-------------------------------------------------------------------------- + | Run artisan commands after migrations to generate model helpers + |-------------------------------------------------------------------------- + | + | The specified commands should run after migrations are finished running. + | + */ + 'post_migrate' => [ + // 'ide-helper:models --nowrite', + ], + +]; diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..309bd4f --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + level: max + paths: + - src diff --git a/src/Dto/RelationshipModelConfigDto.php b/src/Dto/RelationshipModelConfigDto.php new file mode 100644 index 0000000..de34186 --- /dev/null +++ b/src/Dto/RelationshipModelConfigDto.php @@ -0,0 +1,57 @@ +relationshipType = $relationshipType; + $this->service = $service; + $this->isRead = $read; + $this->isWrite = $write; + $this->isNullable = $nullable; + } + + public function getRelationshipType(): string + { + return $this->relationshipType; + } + + public function getService(): string + { + return $this->service; + } + + public function isRead(): bool + { + return $this->isRead; + } + + public function isWrite(): bool + { + return $this->isWrite; + } + + public function isNullable(): bool + { + return $this->isNullable; + } +} diff --git a/src/Exceptions/IdeHelperException.php b/src/Exceptions/IdeHelperException.php new file mode 100644 index 0000000..0be3f4c --- /dev/null +++ b/src/Exceptions/IdeHelperException.php @@ -0,0 +1,11 @@ +modelRelationshipService = $modelRelationshipService; + $this->modelJsonableService = $modelJsonableService; + } + + /** + * @throws IdeHelperException + */ + public function run(ModelsCommand $command, Model $model): void + { + /** @var \Model $model */ + $this->modelRelationshipService->serve($command, $model); + $this->modelJsonableService->serve($command, $model); + } +} diff --git a/src/IdeHelperServiceProvider.php b/src/IdeHelperServiceProvider.php new file mode 100644 index 0000000..b8e78a8 --- /dev/null +++ b/src/IdeHelperServiceProvider.php @@ -0,0 +1,45 @@ +app->make('events'); + /** @var \October\Rain\Config\Repository $config */ + $config = $this->app->make('config'); + + if (!$this->app->runningUnitTests() && $config->get('ide-helper.post_migrate', [])) { + $events->listen(CommandFinished::class, GenerateModelHelper::class); + $events->listen(MigrationsEnded::class, function () { + GenerateModelHelper::$shouldRun = true; + }); + } + + if ($this->app->has('view')) { + $viewPath = __DIR__ . '/../resources/views'; + $this->loadViewsFrom($viewPath, 'ide-helper'); + } + + $configPath = __DIR__ . '/../config/ide-helper.php'; + if (function_exists('config_path')) { + $publishPath = config_path('ide-helper.php'); + } else { + $publishPath = $this->app->basePath('config/ide-helper.php'); + } + + $this->publishes([$configPath => $publishPath], 'config'); + } +} diff --git a/src/Services/ModelJsonableService.php b/src/Services/ModelJsonableService.php new file mode 100644 index 0000000..53f42dc --- /dev/null +++ b/src/Services/ModelJsonableService.php @@ -0,0 +1,23 @@ +getJsonable(); + + foreach ($columns as $column) { + $isNullable = Tools::isNullable($modelsCommand, $column); + + $modelsCommand->setProperty($column, 'array', true, true, '', $isNullable); + } + } +} diff --git a/src/Services/ModelRelationships/BelongsToModelRelationshipService.php b/src/Services/ModelRelationships/BelongsToModelRelationshipService.php new file mode 100644 index 0000000..c661a19 --- /dev/null +++ b/src/Services/ModelRelationships/BelongsToModelRelationshipService.php @@ -0,0 +1,69 @@ +getMethodType($model, $relationshipModelConfigDto->getRelationshipType()); + $relationshipColumnName = $this->getRelationshipColumnName($parameters); + $isNullable = Tools::isNullable($modelsCommand, $relationshipColumnName); + + $modelsCommand->setProperty( + $relationship, + $relationshipClass, + $relationshipModelConfigDto->isRead(), + $relationshipModelConfigDto->isWrite(), + '', + $isNullable + ); + $modelsCommand->setMethod($relationship, $methodType); + } + + /** + * @param mixed $parameters + * @throws IdeHelperException + */ + protected function getRelationshipColumnName($parameters): string + { + $relationshipColumnName = is_array($parameters) ? Arr::get($parameters, 'key') : null; + $relationshipColumnName = is_string($relationshipColumnName) ? $relationshipColumnName : Model::class; + + if (!empty($relationshipColumnName)) { + return $relationshipColumnName; + } + + $modelClass = Tools::getModelClass($parameters); + + $relationshipColumnName = (string)substr($modelClass, strrpos($modelClass, '\\') + 1); + $relationshipColumnName = (string)preg_replace( + '/(? [ + 'service' => SingleModelRelationshipService::class, + 'relationship_type' => AttachOne::class, + ], + 'attachMany' => [ + 'service' => MultipleModelRelationshipService::class, + 'relationship_type' => AttachMany::class, + ], + 'hasOne' => [ + 'service' => SingleModelRelationshipService::class, + 'relationship_type' => HasOne::class, + ], + 'hasMany' => [ + 'service' => MultipleModelRelationshipService::class, + 'relationship_type' => HasMany::class, + ], + 'belongsToMany' => [ + 'service' => MultipleModelRelationshipService::class, + 'relationship_type' => BelongsToMany::class, + ], + 'belongsTo' => [ + 'service' => BelongsToModelRelationshipService::class, + 'relationship_type' => BelongsTo::class, + ], + 'hasOneThrough' => [ + 'service' => SingleModelRelationshipService::class, + 'relationship_type' => HasOneThrough::class, + ], + 'hasManyThrough' => [ + 'service' => MultipleModelRelationshipService::class, + 'relationship_type' => HasManyThrough::class, + ], + 'morphOne' => [ + 'service' => MultipleModelRelationshipService::class, + 'relationship_type' => MorphOne::class, + ], + 'morphMany' => [ + 'service' => MultipleModelRelationshipService::class, + 'relationship_type' => MorphMany::class, + ], + 'morphToMany' => [ + 'service' => MultipleModelRelationshipService::class, + 'relationship_type' => MorphToMany::class, + ], + 'morphTo' => [ + 'service' => MorphToModelRelationshipService::class, + 'relationship_type' => MorphTo::class, + ], + ]; + + /** @var Application */ + private $app; + + public function __construct(Application $app) + { + $this->app = $app; + } + + /** + * @throws IdeHelperException|BindingResolutionException + */ + public function serve(ModelsCommand $modelsCommand, Model $model): void + { + foreach (self::RELATIONSHIPS as $relationType => $config) { + $relationships = $model->{$relationType}; + + if (empty($relationships)) { + continue; + } + + $relationshipModelConfigDto = $this->getRelationshipModelConfigDto($config); + $service = $this->getService($relationshipModelConfigDto); + + /** @var string $relationship */ + /** @var mixed $parameters */ + foreach ($relationships as $relationship => $parameters) { + $service->serve($modelsCommand, $model, $relationship, $parameters, $relationshipModelConfigDto); + } + } + } + + /** + * @param array $config + * @return RelationshipModelConfigDto + * @throws IdeHelperException + */ + private function getRelationshipModelConfigDto(array $config): RelationshipModelConfigDto + { + $relationshipType = Arr::get($config, 'relationship_type'); + + if (!is_string($relationshipType) || empty($relationshipType)) { + throw new IdeHelperException('Relation type does not exist'); + } + + $service = Arr::get($config, 'service'); + + if (!is_string($service) || empty($service)) { + throw new IdeHelperException('Service does not exist'); + } + + $relationshipModelConfigDto = new RelationshipModelConfigDto( + Tools::getClassFormat($relationshipType), + $service, + (bool)Arr::get($config, 'read', true), + (bool)Arr::get($config, 'write', false), + (bool)Arr::get($config, 'nullable', true) + ); + + return $relationshipModelConfigDto; + } + + /** + * @throws IdeHelperException + * @throws BindingResolutionException + */ + private function getService( + RelationshipModelConfigDto $relationshipModelConfigDto + ): ModelRelationshipServiceInterface { + if (!class_exists($relationshipModelConfigDto->getService())) { + throw new IdeHelperException( + sprintf( + 'Service class %s does not exist', + $relationshipModelConfigDto->getService() + ) + ); + } + + /** @var ModelRelationshipServiceInterface $service */ + $service = $this->app->make($relationshipModelConfigDto->getService()); + + return $service; + } +} diff --git a/src/Services/ModelRelationships/ModelRelationshipServiceInterface.php b/src/Services/ModelRelationships/ModelRelationshipServiceInterface.php new file mode 100644 index 0000000..3caa15e --- /dev/null +++ b/src/Services/ModelRelationships/ModelRelationshipServiceInterface.php @@ -0,0 +1,23 @@ +getMethodType($model, $relationshipModelConfigDto->getRelationshipType()); + $relationshipColumnName = sprintf('%s_id', strtolower($relationship)); + $isNullable = Tools::isNullable($modelsCommand, $relationshipColumnName); + + $modelsCommand->setProperty( + $relationship, + $relationshipClass, + $relationshipModelConfigDto->isRead(), + $relationshipModelConfigDto->isWrite(), + '', + $isNullable + ); + $modelsCommand->setMethod($relationship, $methodType); + } +} diff --git a/src/Services/ModelRelationships/MultipleModelRelationshipService.php b/src/Services/ModelRelationships/MultipleModelRelationshipService.php new file mode 100644 index 0000000..3acd655 --- /dev/null +++ b/src/Services/ModelRelationships/MultipleModelRelationshipService.php @@ -0,0 +1,39 @@ +getMethodType($model, $relationshipModelConfigDto->getRelationshipType()); + + $modelsCommand->setProperty( + $relationship, + $relationshipClass, + $relationshipModelConfigDto->isRead(), + $relationshipModelConfigDto->isWrite(), + '', + $relationshipModelConfigDto->isNullable() + ); + $modelsCommand->setMethod($relationship, $methodType); + } +} diff --git a/src/Services/ModelRelationships/SingleModelRelationshipService.php b/src/Services/ModelRelationships/SingleModelRelationshipService.php new file mode 100644 index 0000000..b2886ba --- /dev/null +++ b/src/Services/ModelRelationships/SingleModelRelationshipService.php @@ -0,0 +1,39 @@ +getMethodType($model, $relationshipModelConfigDto->getRelationshipType()); + + $modelsCommand->setProperty( + $relationship, + $relationshipClass, + $relationshipModelConfigDto->isRead(), + $relationshipModelConfigDto->isWrite(), + '', + $relationshipModelConfigDto->isNullable() + ); + $modelsCommand->setMethod($relationship, $methodType); + } +} diff --git a/src/Tools/Tools.php b/src/Tools/Tools.php new file mode 100644 index 0000000..7a4079c --- /dev/null +++ b/src/Tools/Tools.php @@ -0,0 +1,76 @@ +', $collectionClass, $modelClass); + } + + public static function isNullable(ModelsCommand $modelsCommand, ?string $column): bool + { + if (empty($column)) { + return false; + } + + $reflectionClass = new ReflectionClass($modelsCommand); + + $property = $reflectionClass->getProperty('nullableColumns'); + $property->setAccessible(true); + + $nullableColumns = $property->getValue($modelsCommand); + $nullableColumns = is_array($nullableColumns) ? $nullableColumns : []; + $nullableColumns = array_filter($nullableColumns); + + return isset($nullableColumns[$column]); + } +}