From 580ef1796d830250b7b40b5b10f7fb9da3f2ee0d Mon Sep 17 00:00:00 2001 From: Albert Borsos Date: Wed, 26 Sep 2018 10:35:29 +0200 Subject: [PATCH] Improvements (#1) * remove inaccurate description of process method from docblock and fix return type * remove unnecessary resource related parts from gii template * preload correct model namespace * rename AbstractDomain to AbstractService because it is more relevant naming * update readme for new classes and naming convention * update crud templates * rename test methods in templates * allow to configure test path in crud template * mark deprecated classes * rename domains to services in test templates * update module name default placeholder in ns property of model generator --- README.md | 148 +++-------- src/data/ResourceDataProvider.php | 1 + src/gii/generators/crud/Generator.php | 234 +++++++++--------- .../generators/crud/default/controller.php | 42 ++-- .../crud/default/domains/abstract.form.php | 26 -- .../crud/default/domains/create.domain.php | 34 --- .../crud/default/domains/create.form.php | 5 +- .../crud/default/domains/create.service.php | 36 +++ .../crud/default/domains/delete.form.php | 5 +- .../{delete.domain.php => delete.service.php} | 23 +- .../default/domains/toggle-status.form.php | 6 +- ...s.domain.php => toggle-status.service.php} | 30 +-- .../crud/default/domains/update.domain.php | 34 --- .../crud/default/domains/update.form.php | 6 +- .../crud/default/domains/update.service.php | 36 +++ .../default/tests/unit/create.form.test.php | 7 +- ...omain.test.php => create.service.test.php} | 15 +- .../default/tests/unit/delete.form.test.php | 7 +- ...omain.test.php => delete.service.test.php} | 12 +- ...est.php => toggle-status.service.test.php} | 12 +- .../default/tests/unit/update.form.test.php | 7 +- ...omain.test.php => update.service.test.php} | 15 +- .../generators/crud/default/views/index.php | 3 - src/gii/generators/crud/form.php | 2 + src/gii/generators/model/Generator.php | 41 +-- src/gii/generators/model/default/query.php | 2 +- src/gii/generators/model/default/resource.php | 75 ------ .../generators/model/tests/resource.test.php | 49 ---- src/grid/DataColumn.php | 5 + src/grid/GridView.php | 5 + src/models/AbstractDomain.php | 5 +- src/models/AbstractResource.php | 1 + src/models/AbstractService.php | 57 +++++ .../base/AbstractBaseResourceTest.php | 5 + tests/_support/base/AbstractDomainTest.php | 5 + tests/_support/base/AbstractServiceTest.php | 53 ++++ 36 files changed, 459 insertions(+), 590 deletions(-) delete mode 100644 src/gii/generators/crud/default/domains/abstract.form.php delete mode 100644 src/gii/generators/crud/default/domains/create.domain.php create mode 100644 src/gii/generators/crud/default/domains/create.service.php rename src/gii/generators/crud/default/domains/{delete.domain.php => delete.service.php} (52%) rename src/gii/generators/crud/default/domains/{toggle-status.domain.php => toggle-status.service.php} (58%) delete mode 100644 src/gii/generators/crud/default/domains/update.domain.php create mode 100644 src/gii/generators/crud/default/domains/update.service.php rename src/gii/generators/crud/default/tests/unit/{create.domain.test.php => create.service.test.php} (74%) rename src/gii/generators/crud/default/tests/unit/{delete.domain.test.php => delete.service.test.php} (81%) rename src/gii/generators/crud/default/tests/unit/{toggle-status.domain.test.php => toggle-status.service.test.php} (84%) rename src/gii/generators/crud/default/tests/unit/{update.domain.test.php => update.service.test.php} (81%) delete mode 100644 src/gii/generators/model/default/resource.php delete mode 100644 src/gii/generators/model/tests/resource.test.php create mode 100644 src/models/AbstractService.php create mode 100644 tests/_support/base/AbstractServiceTest.php diff --git a/README.md b/README.md index 9a0b154e..8f4a9ce8 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,18 @@ DDD Classes for Yii 2.0 ======================= -Classes for Domain-Driven Development with Yii 2.0 Framework +Classes for a Domain-Driven Design inspired workflow with Yii 2.0 Framework Installation ------------ The preferred way to install this extension is through [composer](http://getcomposer.org/download/). -Either run +Run ``` php composer.phar require --prefer-dist albertborsos/yii2-ddd ``` -or add - -``` -"albertborsos/yii2-ddd": "~0.1" -``` - to the require section of your `composer.json` file. Usage @@ -28,14 +22,14 @@ Lets see an example with a standard `App` model which is an implementation of `\ I recommend to do not make any modification with this class, but make it `abstract` to prevent direct usages. Then create a business model which extends our abstract active record class and implements `BusinessObject` interface. -Every business logic will be implemented into this class. +Every business logic will be implemented in this class. ```php getModel() ?? new App(); - $model->load($this->getForm()->attributes, ''); - - $model = $this->generateApiKey($model); - - if ($model->save()) { - $this->setId($model->id); - return true; - } - - $this->getForm()->addErrors($model->getErrors()); - return false; - } - - /** - * Business logic to update data. - * - * @return bool - */ - protected function update() - { - $model = $this->getModel(); - $model->load($this->getForm()->attributes, ''); - - if ($model->save()) { - $this->setId($model->id); - return true; - } - - $this->getForm()->addErrors($model->getErrors()); - return false; - } - - /** - * Business logic to delete data. - * - * @return bool - */ - public function delete() - { - /** @var App $model */ - $model = $this->getModel(); - $model->delete(); - return true; - } - - private function generateApiKey(App $model) - { - $apiKey = Yii::$app->security->generateRandomString(32); - - if (App::findOne(['api_key' => $apiKey]) === null) { - $newModel = clone $model; - $newModel->api_key = $apiKey; - - return $newModel; - } - - return $this->generateApiKey($model); - } -} - -``` - #### Lets create a new record! We will need a new `FormObject` which will be responsible for the data validation. -And we will need a `domain` model, which handles the business logic with the related models too. +And we will need a `service` model, which handles the business logic with the related models too. -A simple example to a `FormObject`: +A simple example for a `FormObject`: ```php getForm()); - if ($resource->save()) { - $this->assignLanguages($resource->getId(), $this->getForm()->languages); - $this->setId($resource->getId()); - + $model = new App(); + $model->load($this->getForm()->attributes, ''); + + if ($model->save()) { + $this->assignLanguages($model->getId(), $this->getForm()->languages); + $this->setId($model->getId()); + return true; } } catch(\yii\db\Exception $e) { @@ -208,8 +120,8 @@ class CreateAppDomain extends AbstractDomain throw new Exception('Unable to validate language for this app'); } - $domain = new CreateAppLanguageDomain($form); - if ($domain->process() === false) { + $service = new CreateAppLanguageService($form); + if ($service->execute() === false) { throw new Exception('Unable to save language for this app'); } } @@ -223,11 +135,11 @@ And this is how you can use it in the controller ```php load(Yii::$app->request->post()) && $form->validate()) { - $domain = new CreateAppDomain($form); - if ($domain->process()) { + $service = new CreateAppService($form); + if ($service->execute()) { AlertWidget::addSuccess('App created successfully!'); - return $this->redirect(['view', 'id' => $domain->getId()]); + return $this->redirect(['view', 'id' => $service->getId()]); } } diff --git a/src/data/ResourceDataProvider.php b/src/data/ResourceDataProvider.php index fd9410ef..dd5f05d1 100644 --- a/src/data/ResourceDataProvider.php +++ b/src/data/ResourceDataProvider.php @@ -6,6 +6,7 @@ /** * Class ResourceDataProvider + * @deprecated since 0.3.0, will be removed in 1.0.0. * @package albertborsos\ddd\data */ class ResourceDataProvider extends ActiveDataProvider diff --git a/src/gii/generators/crud/Generator.php b/src/gii/generators/crud/Generator.php index e090eebf..99f45b2a 100644 --- a/src/gii/generators/crud/Generator.php +++ b/src/gii/generators/crud/Generator.php @@ -12,6 +12,7 @@ use yii\db\BaseActiveRecord; use yii\db\Schema; use yii\gii\CodeFile; +use yii\helpers\FileHelper; use yii\helpers\Inflector; use yii\helpers\StringHelper; use yii\helpers\VarDumper; @@ -38,6 +39,9 @@ class Generator extends \yii\gii\Generator public $baseControllerClass = 'yii\web\Controller'; public $indexWidgetType = 'grid'; public $searchModelClass = ''; + public $generateTests = false; + public $testPath = '@app/tests'; + /** * @var boolean whether to wrap the `GridView` or `ListView` widget with the `yii\widgets\Pjax` widget * @since 2.0.5 @@ -69,7 +73,7 @@ public function rules() { return array_merge(parent::rules(), [ [['controllerClass', 'modelClass', 'searchModelClass', 'baseControllerClass'], 'filter', 'filter' => 'trim'], - [['modelClass', 'controllerClass', 'baseControllerClass', 'indexWidgetType'], 'required'], + [['modelClass', 'controllerClass', 'baseControllerClass', 'indexWidgetType', 'testPath'], 'required'], [['searchModelClass'], 'compare', 'compareAttribute' => 'modelClass', 'operator' => '!==', 'message' => 'Search Model Class must not be equal to Model Class.'], [['modelClass', 'controllerClass', 'baseControllerClass', 'searchModelClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], [['modelClass'], 'validateClass', 'params' => ['extends' => BaseActiveRecord::className()]], @@ -80,8 +84,10 @@ public function rules() [['indexWidgetType'], 'in', 'range' => ['grid', 'list']], [['modelClass'], 'validateModelClass'], [['enableI18N', 'enablePjax'], 'boolean'], + [['generateTests'], 'boolean'], [['messageCategory'], 'validateMessageCategory', 'skipOnEmpty' => false], ['viewPath', 'safe'], + ['testPath', 'safe'], ]); } @@ -104,6 +110,26 @@ public function validateClass($attribute, $params) return false; } + /** + * An inline validator that checks if the attribute value refers to a valid namespaced class name. + * The validator will check if the directory containing the new class file exist or not. + * @param string $attribute the attribute being validated + * @param array $params the validation options + * @throws \yii\base\Exception + */ + public function validateNewClass($attribute, $params) + { + $class = ltrim($this->$attribute, '\\'); + if (($pos = strrpos($class, '\\')) !== false) { + $ns = substr($class, 0, $pos); + $path = Yii::getAlias('@' . str_replace('\\', '/', $ns), false); + if ($path && !is_dir($path)) { + FileHelper::createDirectory($path); + } + } + parent::validateNewClass($attribute, $params); + } + /** * @inheritdoc */ @@ -113,10 +139,12 @@ public function attributeLabels() 'modelClass' => 'Model Class', 'controllerClass' => 'Controller Class', 'viewPath' => 'View Path', + 'testPath' => 'Test Path', 'baseControllerClass' => 'Base Controller Class', 'indexWidgetType' => 'Widget Used in Index Page', 'searchModelClass' => 'Search Model Class', 'enablePjax' => 'Enable Pjax', + 'generateTests' => 'Generate test files', ]); } @@ -135,6 +163,8 @@ public function hints() 'viewPath' => 'Specify the directory for storing the view scripts for the controller. You may use path alias here, e.g., /var/www/basic/controllers/views/post, @app/views/post. If not set, it will default to @app/views/ControllerID', + 'testPath' => 'Specify the directory for storing the test scripts for the controller. You may use path alias here, e.g., + @app/tests/codeception.', 'baseControllerClass' => 'This is the class that the new CRUD controller class will extend from. You should provide a fully qualified class name, e.g., yii\web\Controller.', 'indexWidgetType' => 'This is the widget type to be used in the index page to display list of the models. @@ -160,7 +190,7 @@ public function requiredTemplates() */ public function stickyAttributes() { - return array_merge(parent::stickyAttributes(), ['baseControllerClass', 'indexWidgetType']); + return array_merge(parent::stickyAttributes(), ['baseControllerClass', 'indexWidgetType', 'testPath']); } /** @@ -182,24 +212,24 @@ public function validateModelClass() public function generate() { $controllerFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->controllerClass, '\\')) . '.php'); - $abstractFormFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getAbstractFormClass(), '\\')) . '.php'); - $createDomainFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getCreateDomainClass(), '\\')) . '.php'); - $createDomainTestFile = ltrim($this->getTestFilePath($this->getCreateDomainClass()), '\\') . '.php'; + $createServiceFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getCreateServiceClass(), '\\')) . '.php'); $createFormFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getCreateFormClass(), '\\')) . '.php'); - $createFormTestFile = ltrim($this->getTestFilePath($this->getCreateFormClass()), '\\') . '.php'; - $updateDomainFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getUpdateDomainClass(), '\\')) . '.php'); - $updateDomainTestFile = ltrim($this->getTestFilePath($this->getUpdateDomainClass()), '\\') . '.php'; + $updateServiceFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getUpdateServiceClass(), '\\')) . '.php'); $updateFormFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getUpdateFormClass(), '\\')) . '.php'); - $updateFormTestFile = ltrim($this->getTestFilePath($this->getUpdateFormClass()), '\\') . '.php'; - $deleteDomainFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getDeleteDomainClass(), '\\')) . '.php'); - $deleteDomainTestFile = ltrim($this->getTestFilePath($this->getDeleteDomainClass()), '\\') . '.php'; + $deleteServiceFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getDeleteServiceClass(), '\\')) . '.php'); $deleteFormFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getDeleteFormClass(), '\\')) . '.php'); - $deleteFormTestFile = ltrim($this->getTestFilePath($this->getDeleteFormClass()), '\\') . '.php'; - $fixtureDataFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getUnitTestFixtureDataFile(), '\\')) . '.php'); - $fixtureClassFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getUnitTestFixtureClass(), '\\')) . '.php'); - $toggleStatusDomainFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getToggleStatusDomainClass(), '\\')) . '.php'); + $toggleStatusServiceFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getToggleStatusServiceClass(), '\\')) . '.php'); $toggleStatusFormFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getToggleStatusFormClass(), '\\')) . '.php'); - $toggleStatusDomainTestFile = ltrim($this->getTestFilePath($this->getToggleStatusDomainClass()), '\\') . '.php'; + + $fixtureDataFile = $this->getUnitTestFixtureDataFile() . '.php'; + $fixtureClassFile = $this->getUnitTestFixtureClass() . '.php'; + $createServiceTestFile = ltrim($this->getTestFilePath($this->getCreateServiceClass()), '\\') . '.php'; + $createFormTestFile = ltrim($this->getTestFilePath($this->getCreateFormClass()), '\\') . '.php'; + $updateServiceTestFile = ltrim($this->getTestFilePath($this->getUpdateServiceClass()), '\\') . '.php'; + $updateFormTestFile = ltrim($this->getTestFilePath($this->getUpdateFormClass()), '\\') . '.php'; + $deleteServiceTestFile = ltrim($this->getTestFilePath($this->getDeleteServiceClass()), '\\') . '.php'; + $deleteFormTestFile = ltrim($this->getTestFilePath($this->getDeleteFormClass()), '\\') . '.php'; + $toggleStatusServiceTestFile = ltrim($this->getTestFilePath($this->getToggleStatusServiceClass()), '\\') . '.php'; $files = [ new CodeFile($controllerFile, $this->render('controller.php')), @@ -211,30 +241,39 @@ public function generate() } $files = array_merge($files, [ - new CodeFile($abstractFormFile, $this->render('domains/abstract.form.php')), new CodeFile($createFormFile, $this->render('domains/create.form.php')), new CodeFile($updateFormFile, $this->render('domains/update.form.php')), new CodeFile($deleteFormFile, $this->render('domains/delete.form.php')), - new CodeFile($createDomainFile, $this->render('domains/create.domain.php')), - new CodeFile($updateDomainFile, $this->render('domains/update.domain.php')), - new CodeFile($deleteDomainFile, $this->render('domains/delete.domain.php')), - //tests - new CodeFile($fixtureClassFile, $this->render('tests/unit/fixture.class.php')), - new CodeFile($fixtureDataFile, $this->render('tests/unit/fixture.php')), - new CodeFile($createFormTestFile, $this->render('tests/unit/create.form.test.php')), - new CodeFile($updateFormTestFile, $this->render('tests/unit/update.form.test.php')), - new CodeFile($deleteFormTestFile, $this->render('tests/unit/delete.form.test.php')), - new CodeFile($createDomainTestFile, $this->render('tests/unit/create.domain.test.php')), - new CodeFile($updateDomainTestFile, $this->render('tests/unit/update.domain.test.php')), - new CodeFile($deleteDomainTestFile, $this->render('tests/unit/delete.domain.test.php')), + new CodeFile($createServiceFile, $this->render('domains/create.service.php')), + new CodeFile($updateServiceFile, $this->render('domains/update.service.php')), + new CodeFile($deleteServiceFile, $this->render('domains/delete.service.php')), ]); + if ($this->generateTests) { + $files = array_merge($files, [ + //tests + new CodeFile($fixtureClassFile, $this->render('tests/unit/fixture.class.php')), + new CodeFile($fixtureDataFile, $this->render('tests/unit/fixture.php')), + new CodeFile($createFormTestFile, $this->render('tests/unit/create.form.test.php')), + new CodeFile($updateFormTestFile, $this->render('tests/unit/update.form.test.php')), + new CodeFile($deleteFormTestFile, $this->render('tests/unit/delete.form.test.php')), + new CodeFile($createServiceTestFile, $this->render('tests/unit/create.service.test.php')), + new CodeFile($updateServiceTestFile, $this->render('tests/unit/update.service.test.php')), + new CodeFile($deleteServiceTestFile, $this->render('tests/unit/delete.service.test.php')), + ]); + } + if (in_array('status', $this->getColumnNames())) { $files = array_merge($files, [ - new CodeFile($toggleStatusDomainFile, $this->render('domains/toggle-status.domain.php')), + new CodeFile($toggleStatusServiceFile, $this->render('domains/toggle-status.service.php')), new CodeFile($toggleStatusFormFile, $this->render('domains/toggle-status.form.php')), - new CodeFile($toggleStatusDomainTestFile, $this->render('tests//unit/toggle-status.domain.test.php')), ]); + + if ($this->generateTests) { + $files = array_merge($files, [ + new CodeFile($toggleStatusServiceTestFile, $this->render('tests//unit/toggle-status.service.test.php')), + ]); + } } $viewPath = $this->getViewPath(); @@ -616,18 +655,15 @@ public function getColumnNames() } } - public function getCreateDomainClass($namespaceOnly = false) + public function getCreateServiceClass($namespaceOnly = false) { $parts = StringHelper::explode($this->modelClass, '\\'); // replace `resources` to `domains` - $resourceKey = array_search('resources', $parts); - $parts[$resourceKey] = 'domains'; - // unset `business` - $businessKey = array_search('business', $parts); - unset($parts[$businessKey]); + $parts = $this->replacePart($parts, 'domains', 'services'); + $parts = $this->replacePart($parts, 'business', null); // replace className with domain name array_pop($parts); - $parts[] = 'Create' . StringHelper::basename($this->modelClass) . 'Domain'; + $parts[] = 'Create' . StringHelper::basename($this->modelClass) . 'Service'; if ($namespaceOnly) { array_pop($parts); @@ -639,12 +675,8 @@ public function getCreateDomainClass($namespaceOnly = false) public function getCreateFormClass($namespaceOnly = false) { $parts = StringHelper::explode($this->modelClass, '\\'); - // replace `resources` to `domains` - $resourceKey = array_search('resources', $parts); - $parts[$resourceKey] = 'domains'; - // replace `business` to `forms` - $businessKey = array_search('business', $parts); - $parts[$businessKey] = 'forms'; + $parts = $this->replacePart($parts, 'domains', 'services'); + $parts = $this->replacePart($parts, 'business', 'forms'); // replace className with forms name array_pop($parts); $parts[] = 'Create' . StringHelper::basename($this->modelClass) . 'Form'; @@ -655,18 +687,14 @@ public function getCreateFormClass($namespaceOnly = false) return implode('\\', $parts); } - public function getToggleStatusDomainClass($namespaceOnly = false) + public function getToggleStatusServiceClass($namespaceOnly = false) { $parts = StringHelper::explode($this->modelClass, '\\'); - // replace `resources` to `domains` - $resourceKey = array_search('resources', $parts); - $parts[$resourceKey] = 'domains'; - // unset `business` - $businessKey = array_search('business', $parts); - unset($parts[$businessKey]); + $parts = $this->replacePart($parts, 'domains', 'services'); + $parts = $this->replacePart($parts, 'business', null); // replace className with domain name array_pop($parts); - $parts[] = 'Toggle' . StringHelper::basename($this->modelClass) . 'StatusDomain'; + $parts[] = 'Toggle' . StringHelper::basename($this->modelClass) . 'StatusService'; if ($namespaceOnly) { array_pop($parts); @@ -678,12 +706,8 @@ public function getToggleStatusDomainClass($namespaceOnly = false) public function getToggleStatusFormClass($namespaceOnly = false) { $parts = StringHelper::explode($this->modelClass, '\\'); - // replace `resources` to `domains` - $resourceKey = array_search('resources', $parts); - $parts[$resourceKey] = 'domains'; - // replace `business` to `forms` - $businessKey = array_search('business', $parts); - $parts[$businessKey] = 'forms'; + $parts = $this->replacePart($parts, 'domains', 'services'); + $parts = $this->replacePart($parts, 'business', 'forms'); // replace className with forms name array_pop($parts); $parts[] = 'Toggle' . StringHelper::basename($this->modelClass) . 'StatusForm'; @@ -695,18 +719,14 @@ public function getToggleStatusFormClass($namespaceOnly = false) return implode('\\', $parts); } - public function getUpdateDomainClass($namespaceOnly = false) + public function getUpdateServiceClass($namespaceOnly = false) { $parts = StringHelper::explode($this->modelClass, '\\'); - // replace `resources` to `domains` - $resourceKey = array_search('resources', $parts); - $parts[$resourceKey] = 'domains'; - // unset `business` - $businessKey = array_search('business', $parts); - unset($parts[$businessKey]); + $parts = $this->replacePart($parts, 'domains', 'services'); + $parts = $this->replacePart($parts, 'business', null); // replace className with domain name array_pop($parts); - $parts[] = 'Update' . StringHelper::basename($this->modelClass) . 'Domain'; + $parts[] = 'Update' . StringHelper::basename($this->modelClass) . 'Service'; if ($namespaceOnly) { array_pop($parts); @@ -718,12 +738,8 @@ public function getUpdateDomainClass($namespaceOnly = false) public function getUpdateFormClass($namespaceOnly = false) { $parts = StringHelper::explode($this->modelClass, '\\'); - // replace `resources` to `domains` - $resourceKey = array_search('resources', $parts); - $parts[$resourceKey] = 'domains'; - // replace `business` to `forms` - $businessKey = array_search('business', $parts); - $parts[$businessKey] = 'forms'; + $parts = $this->replacePart($parts, 'domains', 'services'); + $parts = $this->replacePart($parts, 'business', 'forms'); // replace className with forms name array_pop($parts); $parts[] = 'Update' . StringHelper::basename($this->modelClass) . 'Form'; @@ -735,18 +751,14 @@ public function getUpdateFormClass($namespaceOnly = false) return implode('\\', $parts); } - public function getDeleteDomainClass($namespaceOnly = false) + public function getDeleteServiceClass($namespaceOnly = false) { $parts = StringHelper::explode($this->modelClass, '\\'); - // replace `resources` to `domains` - $resourceKey = array_search('resources', $parts); - $parts[$resourceKey] = 'domains'; - // unset `business` - $businessKey = array_search('business', $parts); - unset($parts[$businessKey]); + $parts = $this->replacePart($parts, 'domains', 'services'); + $parts = $this->replacePart($parts, 'business', null); // replace className with domain name array_pop($parts); - $parts[] = 'Delete' . StringHelper::basename($this->modelClass) . 'Domain'; + $parts[] = 'Delete' . StringHelper::basename($this->modelClass) . 'Service'; if ($namespaceOnly) { array_pop($parts); @@ -758,12 +770,8 @@ public function getDeleteDomainClass($namespaceOnly = false) public function getDeleteFormClass($namespaceOnly = false) { $parts = StringHelper::explode($this->modelClass, '\\'); - // replace `resources` to `domains` - $resourceKey = array_search('resources', $parts); - $parts[$resourceKey] = 'domains'; - // replace `business` to `forms` - $businessKey = array_search('business', $parts); - $parts[$businessKey] = 'forms'; + $parts = $this->replacePart($parts, 'domains', 'services'); + $parts = $this->replacePart($parts, 'business', 'forms'); // replace className with forms name array_pop($parts); $parts[] = 'Delete' . StringHelper::basename($this->modelClass) . 'Form'; @@ -778,9 +786,7 @@ public function getDeleteFormClass($namespaceOnly = false) public function getResourceClass($namespaceOnly = false) { $parts = StringHelper::explode($this->modelClass, '\\'); - // unset `business` - $businessKey = array_search('business', $parts); - unset($parts[$businessKey]); + $parts = $this->replacePart($parts, 'business', null); // replace className with resource name array_pop($parts); $parts[] = StringHelper::basename($this->modelClass) . 'Resource'; @@ -792,49 +798,51 @@ public function getResourceClass($namespaceOnly = false) return implode('\\', $parts); } - public function getAbstractFormClass($namespaceOnly = false) - { - $parts = StringHelper::explode($this->modelClass, '\\'); - // unset `business` - $businessKey = array_search('business', $parts); - unset($parts[$businessKey]); - // replace className with resource name - array_pop($parts); - $parts[] = 'Abstract' . StringHelper::basename($this->modelClass) . 'Form'; - - if ($namespaceOnly) { - array_pop($parts); - } - - return implode('\\', $parts); - } - private function getUnitTestFixtureDataFile() { - $parts[] = 'app\\tests\\codeception\\unit\\fixtures\\data'; + $parts[] = rtrim(Yii::getAlias($this->testPath), '/'); + $parts[] = 'unit/fixtures/data'; $parts[] = strtolower(StringHelper::basename($this->modelClass)); - return implode('\\', $parts); + return implode('/', $parts); } private function getUnitTestFixtureClass() { - $parts[] = 'app\\tests\\codeception\\unit\\fixtures'; + $parts[] = rtrim(Yii::getAlias($this->testPath), '/'); + $parts[] = 'unit/fixtures'; $parts[] = StringHelper::basename($this->modelClass) . 'Fixture'; - return implode('\\', $parts); + return implode('/', $parts); } public function getTestFilePath($mainClassName) { $path = Yii::getAlias('@' . str_replace('\\', '/', $mainClassName)); + $testNs = trim($this->testPath, '@/'); $parts = StringHelper::explode($path, '/'); - // replace `app` to `test` - $resourceKey = array_search('app', $parts); - $parts[$resourceKey] = 'app/tests/codeception/unit'; + $parts = $this->replacePart($parts, 'app', $testNs . '/unit'); array_pop($parts); $parts[] = StringHelper::basename($mainClassName) . 'Test'; return implode('/', $parts); } + + /** + * @param $parts + * @return mixed + */ + private function replacePart($parts, $keyFrom, $keyTo) + { + $keyFromId = array_search($keyFrom, $parts); + if ($keyFromId !== false) { + $parts[$keyFromId] = $keyTo; + } + + if (empty($parts[$keyFromId])) { + unset($parts[$keyFromId]); + } + + return $parts; + } } diff --git a/src/gii/generators/crud/default/controller.php b/src/gii/generators/crud/default/controller.php index 808814f8..0c0d787a 100644 --- a/src/gii/generators/crud/default/controller.php +++ b/src/gii/generators/crud/default/controller.php @@ -17,13 +17,14 @@ if ($modelClass === $searchModelClass) { $searchModelAlias = $searchModelClass . 'Search'; } -$createDomainClass = StringHelper::basename($generator->getCreateDomainClass()); +$createServiceClass = StringHelper::basename($generator->getCreateServiceClass()); $createFormClass = StringHelper::basename($generator->getCreateFormClass()); -$updateDomainClass = StringHelper::basename($generator->getUpdateDomainClass()); +$updateServiceClass = StringHelper::basename($generator->getUpdateServiceClass()); $updateFormClass = StringHelper::basename($generator->getUpdateFormClass()); -$toggleStatusDomainClass = StringHelper::basename($generator->getToggleStatusDomainClass()); +$deleteServiceClass = StringHelper::basename($generator->getDeleteServiceClass()); +$deleteFormClass = StringHelper::basename($generator->getDeleteFormClass()); +$toggleStatusServiceClass = StringHelper::basename($generator->getToggleStatusServiceClass()); $toggleStatusFormClass = StringHelper::basename($generator->getToggleStatusFormClass()); -$resourceClass = StringHelper::basename($generator->getResourceClass()); /* @var $class ActiveRecordInterface */ $class = $generator->modelClass; @@ -51,13 +52,14 @@ use yii\web\Response; use yii\bootstrap\ActiveForm; use yii\filters\AccessControl; -use getCreateDomainClass(), '\\') ?>; +use getCreateServiceClass(), '\\') ?>; +use getUpdateServiceClass(), '\\') ?>; +use getDeleteServiceClass(), '\\') ?>; +use getToggleStatusServiceClass(), '\\') ?>; use getCreateFormClass(), '\\') ?>; -use getUpdateDomainClass(), '\\') ?>; use getUpdateFormClass(), '\\') ?>; -use getToggleStatusDomainClass(), '\\') ?>; +use getDeleteFormClass(), '\\') ?>; use getToggleStatusFormClass(), '\\') ?>; -use getResourceClass(), '\\') ?>; /** * implements the CRUD actions for model. @@ -172,10 +174,10 @@ public function actionCreate() } if ($form->load(Yii::$app->request->post()) && $form->validate()) { - $domain = new ($form); - if ($domain->process()) { + $service = new ($form); + if ($service->execute()) { AlertWidget::addSuccess(' created successfully!'); - return $this->redirect(['view', 'id' => $domain->getId()]); + return $this->redirect(['view', 'id' => $service->getId()]); } } @@ -201,10 +203,10 @@ public function actionUpdate() } if ($form->load(Yii::$app->request->post()) && $form->validate()) { - $domain = new ($form, $model); - if ($domain->process()) { + $service = new ($form, $model); + if ($service->execute()) { AlertWidget::addSuccess(' updated successfully!'); - return $this->redirect(['update', 'id' => $domain->getId()]); + return $this->redirect(['update', 'id' => $service->getId()]); } } @@ -222,15 +224,15 @@ public function actionUpdate() public function actionDelete() { $model = $this->findModel($id); - $form = new getDeleteFormClass())?>($model); + $form = new ($model); if ($form->validate()) { - $domain = new getDeleteDomainClass())?>(null, $model); - if ($domain->process()) { + $service = new (null, $model); + if ($service->execute()) { AlertWidget::addSuccess(' removed successfully!'); return $this->redirect(['index']); } - AlertWidget::addError(Html::errorSummary($domain)); + AlertWidget::addError(Html::errorSummary($service)); return $this->redirect(['index']); } @@ -250,8 +252,8 @@ public function actionStatus() $form = new ($model); if ($form->validate()) { - $domain = new ($form, $model); - if ($domain->process()) { + $service = new ($form, $model); + if ($service->execute()) { AlertWidget::addSuccess(' updated successfully!'); return $this->redirect(['index']); } diff --git a/src/gii/generators/crud/default/domains/abstract.form.php b/src/gii/generators/crud/default/domains/abstract.form.php deleted file mode 100644 index 1891148f..00000000 --- a/src/gii/generators/crud/default/domains/abstract.form.php +++ /dev/null @@ -1,26 +0,0 @@ - - -namespace getAbstractFormClass(true) ?>; - -use \albertborsos\ddd\interfaces\FormObject; -use modelClass) ?>; -use yii\base\Model; - -class getAbstractFormClass()) ?> extends Model implements FormObject -{ - public $id; - - public function attributeLabels() - { - return (new modelClass) ?>())->attributeLabels(); - } -} diff --git a/src/gii/generators/crud/default/domains/create.domain.php b/src/gii/generators/crud/default/domains/create.domain.php deleted file mode 100644 index 2c1544d7..00000000 --- a/src/gii/generators/crud/default/domains/create.domain.php +++ /dev/null @@ -1,34 +0,0 @@ - - -namespace getCreateDomainClass(true) ?>; - -use \albertborsos\ddd\models\AbstractDomain; -use getResourceClass()) ?>; - -class getCreateDomainClass()) ?> extends AbstractDomain -{ - /** - * Business logic to store data for multiple resources. - * - * @return mixed - */ - public function process() - { - $resource = new getResourceClass()) ?>($this->getForm()); - if ($resource->save()) { - $this->setId($resource->getId()); - return true; - } - - return false; - } -} diff --git a/src/gii/generators/crud/default/domains/create.form.php b/src/gii/generators/crud/default/domains/create.form.php index 3ca5709b..0d4b8f2d 100644 --- a/src/gii/generators/crud/default/domains/create.form.php +++ b/src/gii/generators/crud/default/domains/create.form.php @@ -11,10 +11,11 @@ namespace getCreateFormClass(true) ?>; -use getAbstractFormClass()) ?>; +use \yii\base\Model; +use \albertborsos\ddd\interfaces\FormObject; use modelClass) ?>; -class getCreateFormClass()) ?> extends getAbstractFormClass()) . "\n" ?> +class getCreateFormClass()) ?> extends Model implements FormObject { // public $email; diff --git a/src/gii/generators/crud/default/domains/create.service.php b/src/gii/generators/crud/default/domains/create.service.php new file mode 100644 index 00000000..e63300f6 --- /dev/null +++ b/src/gii/generators/crud/default/domains/create.service.php @@ -0,0 +1,36 @@ + + +namespace getCreateServiceClass(true) ?>; + +use \albertborsos\ddd\models\AbstractService; +use modelClass) ?>; + +class getCreateServiceClass()) ?> extends AbstractService +{ + /** + * @return bool + */ + public function execute() + { + $model = new modelClass) ?>(); + $model->load($this->getForm()->attributes, ''); + + if ($model->save()) { + $this->setId($model->getId()); + return true; + } + + $this->getForm()->addErrors($model->getErrors()); + + return false; + } +} diff --git a/src/gii/generators/crud/default/domains/delete.form.php b/src/gii/generators/crud/default/domains/delete.form.php index c37d59c5..7f2d96c2 100644 --- a/src/gii/generators/crud/default/domains/delete.form.php +++ b/src/gii/generators/crud/default/domains/delete.form.php @@ -11,10 +11,11 @@ namespace getDeleteFormClass(true) ?>; -use getAbstractFormClass()) ?>; +use \yii\base\Model; +use \albertborsos\ddd\interfaces\FormObject; use modelClass) ?>; -class getDeleteFormClass()) ?> extends getAbstractFormClass()) . "\n" ?> +class getDeleteFormClass()) ?> extends Model implements FormObject { // public $hasChild; diff --git a/src/gii/generators/crud/default/domains/delete.domain.php b/src/gii/generators/crud/default/domains/delete.service.php similarity index 52% rename from src/gii/generators/crud/default/domains/delete.domain.php rename to src/gii/generators/crud/default/domains/delete.service.php index 9b6edd89..87870195 100644 --- a/src/gii/generators/crud/default/domains/delete.domain.php +++ b/src/gii/generators/crud/default/domains/delete.service.php @@ -9,34 +9,33 @@ echo " -namespace getDeleteDomainClass(true) ?>; +namespace getDeleteServiceClass(true) ?>; -use \albertborsos\ddd\models\AbstractDomain; -use getResourceClass()) ?>; +use \albertborsos\ddd\models\AbstractService; -class getDeleteDomainClass()) ?> extends AbstractDomain +class getDeleteServiceClass()) ?> extends AbstractService { /** * Business logic to delete data * * @return mixed */ - public function process() + public function execute() { $transaction = Yii::$app->db->beginTransaction(); try { - $resource = new getResourceClass()) ?>(null, $this->getModel()); - $resource->delete(); - // if ($imageModel = $this->getModel()->image) { - // $imageResource = new ImageResource(null, $imageModel); - // if ($imageResource->delete() === false) { - // throw new Exception('Image cannot be removed!'); + // $service = new DeleteImageService($imageModel); + // if (!$service->execute()) { + // throw new Exception('Image cannot be removed!'); // } // } - $this->setId($resource->getId()); + if (!$this->getModel()->delete()) { + throw new Exception('modelClass) ?> cannot be removed!'); + } $transaction->commit(); + $this->setId($this->getModel()->getId()); return true; } catch (Exception $e) { $transaction->rollBack(); diff --git a/src/gii/generators/crud/default/domains/toggle-status.form.php b/src/gii/generators/crud/default/domains/toggle-status.form.php index aae74692..b02f8f47 100644 --- a/src/gii/generators/crud/default/domains/toggle-status.form.php +++ b/src/gii/generators/crud/default/domains/toggle-status.form.php @@ -13,10 +13,11 @@ namespace getUpdateFormClass(true) ?>; -use getAbstractFormClass()) ?>; +use \yii\base\Model; +use \albertborsos\ddd\interfaces\FormObject; use modelClass) ?>; -class getToggleStatusFormClass()) ?> extends getAbstractFormClass()) . "\n" ?> +class getToggleStatusFormClass()) ?> extends Model implements FormObject { public $status; @@ -28,7 +29,6 @@ public function __construct( private function preloadAttributes(modelClass) ?> $model) { - $this->id = $model->id; $this->status = $model->status; } diff --git a/src/gii/generators/crud/default/domains/toggle-status.domain.php b/src/gii/generators/crud/default/domains/toggle-status.service.php similarity index 58% rename from src/gii/generators/crud/default/domains/toggle-status.domain.php rename to src/gii/generators/crud/default/domains/toggle-status.service.php index 1b44ed2b..ff492b6d 100644 --- a/src/gii/generators/crud/default/domains/toggle-status.domain.php +++ b/src/gii/generators/crud/default/domains/toggle-status.service.php @@ -11,40 +11,40 @@ $modelClassBaseName = \yii\helpers\StringHelper::basename($generator->modelClass); ?> -namespace getToggleStatusDomainClass(true) ?>; +namespace getToggleStatusServiceClass(true) ?>; -use \albertborsos\ddd\models\AbstractDomain; +use \albertborsos\ddd\models\AbstractService; use getToggleStatusFormClass()) ?>; -use getResourceClass()) ?>; use modelClass) ?>; -class getToggleStatusDomainClass()) ?> extends AbstractDomain +class getToggleStatusServiceClass()) ?> extends AbstractService { /** - * Business logic to store data for multiple resources. - * - * @return mixed + * @return bool */ - public function process() + public function execute() { - $form = $this->toggleStatus(); + $form = $this->toggleStatus($this->getForm()); - $resource = new getResourceClass()) ?>($form, $this->getModel()); - if ($resource->save()) { - $this->setId($resource->getId()); + $model = $this->getModel(); + $model->load($form->attributes, ''); + + if ($model->save()) { + $this->setId($model->getId()); return true; } + $this->getForm()->addErrors($model->getErrors()); + return false; } /** * @return getToggleStatusFormClass()) . "\n" ?> */ - private function toggleStatus() + private function toggleStatus(getToggleStatusFormClass()) ?> $form) { - /** @var getToggleStatusFormClass()) ?> $form */ - $form = clone $this->getForm(); + $form = clone $form; switch ($form->status) { case ::STATUS_ACTIVE: $form->status = ::STATUS_INACTIVE; diff --git a/src/gii/generators/crud/default/domains/update.domain.php b/src/gii/generators/crud/default/domains/update.domain.php deleted file mode 100644 index 18d8e18e..00000000 --- a/src/gii/generators/crud/default/domains/update.domain.php +++ /dev/null @@ -1,34 +0,0 @@ - - -namespace getUpdateDomainClass(true) ?>; - -use \albertborsos\ddd\models\AbstractDomain; -use getResourceClass()) ?>; - -class getUpdateDomainClass()) ?> extends AbstractDomain -{ - /** - * Business logic to store data for multiple resources. - * - * @return mixed - */ - public function process() - { - $resource = new getResourceClass()) ?>($this->getForm(), $this->getModel()); - if ($resource->save()) { - $this->setId($resource->getId()); - return true; - } - - return false; - } -} diff --git a/src/gii/generators/crud/default/domains/update.form.php b/src/gii/generators/crud/default/domains/update.form.php index 30e2fe07..a4a73e2d 100644 --- a/src/gii/generators/crud/default/domains/update.form.php +++ b/src/gii/generators/crud/default/domains/update.form.php @@ -11,10 +11,11 @@ namespace getUpdateFormClass(true) ?>; -use getAbstractFormClass()) ?>; +use \yii\base\Model; +use \albertborsos\ddd\interfaces\FormObject; use modelClass) ?>; -class getUpdateFormClass()) ?> extends getAbstractFormClass()) . "\n" ?> +class getUpdateFormClass()) ?> extends Model implements FormObject { // public $email; @@ -26,7 +27,6 @@ public function __construct( private function preloadAttributes(modelClass) ?> $model) { - $this->id = $model->id; // $this->email = $model->email; } diff --git a/src/gii/generators/crud/default/domains/update.service.php b/src/gii/generators/crud/default/domains/update.service.php new file mode 100644 index 00000000..d0d05a4f --- /dev/null +++ b/src/gii/generators/crud/default/domains/update.service.php @@ -0,0 +1,36 @@ + + +namespace getUpdateServiceClass(true) ?>; + +use \albertborsos\ddd\models\AbstractService; +use modelClass) ?>; + +class getUpdateServiceClass()) ?> extends AbstractService +{ + /** + * @return bool + */ + public function execute() + { + $model = $this->getModel(); + $model->load($this->getForm()->attributes, ''); + + if ($model->save()) { + $this->setId($model->getId()); + return true; + } + + $this->getForm()->addErrors($model->getErrors()); + + return false; + } +} diff --git a/src/gii/generators/crud/default/tests/unit/create.form.test.php b/src/gii/generators/crud/default/tests/unit/create.form.test.php index ae384224..8242eecb 100644 --- a/src/gii/generators/crud/default/tests/unit/create.form.test.php +++ b/src/gii/generators/crud/default/tests/unit/create.form.test.php @@ -26,7 +26,7 @@ public function invalidDataProvider() * @skip * @dataProvider invalidDataProvider */ - public function testNotPassFormWithInvalidData($expectedErrorAttribute, $name) + public function testShouldNotPassFormWithInvalidData($expectedErrorAttribute, $name) { /** @var getCreateFormClass() ?> $form */ $form = $this->mockForm([ @@ -34,6 +34,7 @@ public function testNotPassFormWithInvalidData($expectedErrorAttribute, $name) ]); $this->assertFalse($form->validate()); + $this->assertCount(1, $form->getErrors(), \yii\helpers\Json::encode($form->getErrors())); $this->assertArrayHasKey($expectedErrorAttribute, $form->getErrors()); } @@ -48,14 +49,14 @@ public function validDataProvider() * @skip * @dataProvider validDataProvider */ - public function testPassFormWithValidData($name) + public function testShouldPassFormWithValidData($name) { /** @var getCreateFormClass() ?> $form */ $form = $this->mockForm([ 'name' => $name, ]); - $this->assertTrue($form->validate(), \yii\helpers\Html::errorSummary($form)); + $this->assertTrue($form->validate(), \yii\helpers\Json::encode($form->getErrors())); $this->assertEmpty($form->getErrors()); } } diff --git a/src/gii/generators/crud/default/tests/unit/create.domain.test.php b/src/gii/generators/crud/default/tests/unit/create.service.test.php similarity index 74% rename from src/gii/generators/crud/default/tests/unit/create.domain.test.php rename to src/gii/generators/crud/default/tests/unit/create.service.test.php index f47a0f15..ae30f9d3 100644 --- a/src/gii/generators/crud/default/tests/unit/create.domain.test.php +++ b/src/gii/generators/crud/default/tests/unit/create.service.test.php @@ -11,17 +11,16 @@ echo " -class getTestFilePath($generator->getCreateDomainClass())) ?> extends AbstractDomainTest +class getTestFilePath($generator->getCreateServiceClass())) ?> extends AbstractServiceTest { protected $formClass = 'getCreateFormClass() ?>'; - protected $domainClass = 'getCreateDomainClass()?>'; + protected $serviceClass = 'getCreateServiceClass()?>'; protected function tearDown() { $models = modelClass?>::find()->all(); foreach ($models as $model) { - $resource = new getResourceClass()?>(null, $model); - $resource->delete(); + $model->delete(); } parent::tearDown(); } @@ -29,7 +28,7 @@ protected function tearDown() /** * Test only valid events here. * Test invalid events in `getTestFilePath($generator->getCreateFormClass())) ?>`, - * because invalid events should not reach the process method of domain classes. + * because invalid events should not reach the execute method of service classes. */ public function createProvider() { @@ -48,10 +47,10 @@ public function testCreate($name) 'name' => $name, ]); - $domain = $this->mockDomain($form); - $this->assertTrue($domain->process()); + $service = $this->mockService($form); + $this->assertTrue($service->execute()); - $model = modelClass ?>::findOne($domain->getId()); + $model = modelClass ?>::findOne($service->getId()); $this->assertEquals($name, $model->name); getColumnNames())) : ?> diff --git a/src/gii/generators/crud/default/tests/unit/delete.form.test.php b/src/gii/generators/crud/default/tests/unit/delete.form.test.php index 11e53556..78304974 100644 --- a/src/gii/generators/crud/default/tests/unit/delete.form.test.php +++ b/src/gii/generators/crud/default/tests/unit/delete.form.test.php @@ -34,12 +34,13 @@ public function invalidDataProvider() * @skip * @dataProvider invalidDataProvider */ - public function testNotPassFormWithInvalidData($expectedErrorAttribute, $Alias) + public function testShouldNotPassFormWithInvalidData($expectedErrorAttribute, $Alias) { /** @var getDeleteFormClass() ?> $form */ $form = $this->mockForm([], $this->getModel($this->[$Alias]['id'])); $this->assertFalse($form->validate()); + $this->assertCount(1, $form->getErrors(), \yii\helpers\Json::encode($form->getErrors())); $this->assertArrayHasKey($expectedErrorAttribute, $form->getErrors()); } @@ -54,12 +55,12 @@ public function validDataProvider() * @skip * @dataProvider validDataProvider */ - public function testPassFormWithValidData($Alias) + public function testShouldPassFormWithValidData($Alias) { /** @var getDeleteFormClass() ?> $form */ $form = $this->mockForm([], $this->getModel($this->[$Alias]['id'])); - $this->assertTrue($form->validate(), \yii\helpers\Html::errorSummary($form)); + $this->assertTrue($form->validate(), \yii\helpers\Json::encode($form->getErrors())); $this->assertEmpty($form->getErrors()); } } diff --git a/src/gii/generators/crud/default/tests/unit/delete.domain.test.php b/src/gii/generators/crud/default/tests/unit/delete.service.test.php similarity index 81% rename from src/gii/generators/crud/default/tests/unit/delete.domain.test.php rename to src/gii/generators/crud/default/tests/unit/delete.service.test.php index c36cc906..95b22e4e 100644 --- a/src/gii/generators/crud/default/tests/unit/delete.domain.test.php +++ b/src/gii/generators/crud/default/tests/unit/delete.service.test.php @@ -14,10 +14,10 @@ use tests\codeception\unit\fixtures\Fixture; -class getTestFilePath($generator->getDeleteDomainClass())) ?> extends AbstractDomainTest +class getTestFilePath($generator->getDeleteServiceClass())) ?> extends AbstractServiceTest { protected $formClass = 'getDeleteFormClass()?>'; - protected $domainClass = 'getDeleteDomainClass()?>'; + protected $serviceClass = 'getDeleteServiceClass()?>'; protected $modelClass = 'modelClass?>'; public function fixtures() @@ -30,7 +30,7 @@ public function fixtures() /** * Test only valid events here. * Test invalid events in `getTestFilePath($generator->getDeleteFormClass())) ?>`, - * because invalid events should not reach the process method of domain classes. + * because invalid events should not reach the execute method of service classes. */ public function deleteProvider() { @@ -51,9 +51,9 @@ public function testDelete($fixtureAlias) $form = $this->mockForm([], $model); - $domain = $this->mockDomain($form, $model); - $this->assertTrue($domain->process()); - $this->assertEquals($expectedId, $domain->getId()); + $service = $this->mockService($form, $model); + $this->assertTrue($service->execute()); + $this->assertEquals($expectedId, $service->getId()); $model = $this->getModel($expectedId); $this->assertNull($model); diff --git a/src/gii/generators/crud/default/tests/unit/toggle-status.domain.test.php b/src/gii/generators/crud/default/tests/unit/toggle-status.service.test.php similarity index 84% rename from src/gii/generators/crud/default/tests/unit/toggle-status.domain.test.php rename to src/gii/generators/crud/default/tests/unit/toggle-status.service.test.php index d24e80fa..9776997b 100644 --- a/src/gii/generators/crud/default/tests/unit/toggle-status.domain.test.php +++ b/src/gii/generators/crud/default/tests/unit/toggle-status.service.test.php @@ -14,10 +14,10 @@ use tests\codeception\unit\fixtures\Fixture; -class getTestFilePath($generator->getToggleStatusDomainClass())) ?> extends AbstractDomainTest +class getTestFilePath($generator->getToggleStatusServiceClass())) ?> extends AbstractServiceTest { protected $formClass = 'getToggleStatusFormClass()?>'; - protected $domainClass = 'getToggleStatusDomainClass()?>'; + protected $serviceClass = 'getToggleStatusServiceClass()?>'; protected $modelClass = 'modelClass?>'; protected function tearDown() @@ -55,11 +55,11 @@ public function testToToggleStatus($fixtureAlias, $exp $model = $this->getModel($this->[$fixtureAlias]['id']); $form = $this->mockForm([], $model); - $domain = $this->mockDomain($form, $model); - $this->assertTrue($domain->process()); - $this->assertEquals($model->id, $domain->getId()); + $service = $this->mockService($form, $model); + $this->assertTrue($service->execute()); + $this->assertEquals($model->id, $service->getId()); - $model = $this->getModel($domain->getId()); + $model = $this->getModel($service->getId()); $this->assertNotNull($model); $this->assertEquals($expectedStatus, $model->status); diff --git a/src/gii/generators/crud/default/tests/unit/update.form.test.php b/src/gii/generators/crud/default/tests/unit/update.form.test.php index 87799fd0..10c7d145 100644 --- a/src/gii/generators/crud/default/tests/unit/update.form.test.php +++ b/src/gii/generators/crud/default/tests/unit/update.form.test.php @@ -34,7 +34,7 @@ public function invalidDataProvider() * @skip * @dataProvider invalidDataProvider */ - public function testNotPassFormWithInvalidData($expectedErrorAttribute, $Alias, $name) + public function testShouldNotPassFormWithInvalidData($expectedErrorAttribute, $Alias, $name) { /** @var getUpdateFormClass() ?> $form */ $form = $this->mockForm([ @@ -42,6 +42,7 @@ public function testNotPassFormWithInvalidData($expectedErrorAttribute, $getModel($this->[$Alias]['id'])); $this->assertFalse($form->validate()); + $this->assertCount(1, $form->getErrors(), \yii\helpers\Json::encode($form->getErrors())); $this->assertArrayHasKey($expectedErrorAttribute, $form->getErrors()); } @@ -56,14 +57,14 @@ public function validDataProvider() * @skip * @dataProvider validDataProvider */ - public function testPassFormWithValidData($Alias, $name) + public function testShouldPassFormWithValidData($Alias, $name) { /** @var getUpdateFormClass() ?> $form */ $form = $this->mockForm([ 'name' => $name, ], $this->getModel($this->[$Alias]['id'])); - $this->assertTrue($form->validate(), \yii\helpers\Html::errorSummary($form)); + $this->assertTrue($form->validate(), \yii\helpers\Json::encode($form->getErrors())); $this->assertEmpty($form->getErrors()); } } diff --git a/src/gii/generators/crud/default/tests/unit/update.domain.test.php b/src/gii/generators/crud/default/tests/unit/update.service.test.php similarity index 81% rename from src/gii/generators/crud/default/tests/unit/update.domain.test.php rename to src/gii/generators/crud/default/tests/unit/update.service.test.php index 933ba120..7f2a2155 100644 --- a/src/gii/generators/crud/default/tests/unit/update.domain.test.php +++ b/src/gii/generators/crud/default/tests/unit/update.service.test.php @@ -14,18 +14,17 @@ use tests\codeception\unit\fixtures\Fixture; -class getTestFilePath($generator->getUpdateDomainClass())) ?> extends AbstractDomainTest +class getTestFilePath($generator->getUpdateServiceClass())) ?> extends AbstractServiceTest { protected $formClass = 'getUpdateFormClass()?>'; - protected $domainClass = 'getUpdateDomainClass()?>'; + protected $serviceClass = 'getUpdateServiceClass()?>'; protected $modelClass = 'modelClass?>'; protected function tearDown() { $models = modelClass?>::find()->all(); foreach ($models as $model) { - $resource = new getResourceClass()?>(null, $model); - $resource->delete(); + $model->delete(); } parent::tearDown(); } @@ -40,7 +39,7 @@ public function fixtures() /** * Test only valid events here. * Test invalid events in `getTestFilePath($generator->getUpdateFormClass())) ?>`, - * because invalid events should not reach the process method of domain classes. + * because invalid events should not reach the execute method of service classes. */ public function updateProvider() { @@ -65,9 +64,9 @@ public function testUpdate($fixtureAlias, $name) 'name' => $name, ], $model); - $domain = $this->mockDomain($form, $model); - $this->assertTrue($domain->process()); - $this->assertEquals($expectedId, $domain->getId()); + $service = $this->mockService($form, $model); + $this->assertTrue($service->execute()); + $this->assertEquals($expectedId, $service->getId()); $model = $this->getModel($expectedId); $this->assertEquals($name, $model->name); diff --git a/src/gii/generators/crud/default/views/index.php b/src/gii/generators/crud/default/views/index.php index d88674f7..7875bb73 100644 --- a/src/gii/generators/crud/default/views/index.php +++ b/src/gii/generators/crud/default/views/index.php @@ -62,9 +62,6 @@ switch ($column->name) { case 'id': break; - case 'status': - echo " 'statusText',\n"; - break; case 'created_at': case 'updated_at': echo " '" . $column->name . ":datetime',\n"; diff --git a/src/gii/generators/crud/form.php b/src/gii/generators/crud/form.php index 4f59b8b6..2176f235 100644 --- a/src/gii/generators/crud/form.php +++ b/src/gii/generators/crud/form.php @@ -14,4 +14,6 @@ ]); echo $form->field($generator, 'enableI18N')->checkbox(); echo $form->field($generator, 'enablePjax')->checkbox(); +echo $form->field($generator, 'generateTests')->checkbox(); +echo $form->field($generator, 'testPath'); echo $form->field($generator, 'messageCategory'); diff --git a/src/gii/generators/model/Generator.php b/src/gii/generators/model/Generator.php index b8fd200e..73a7b7ff 100644 --- a/src/gii/generators/model/Generator.php +++ b/src/gii/generators/model/Generator.php @@ -31,7 +31,7 @@ class Generator extends \yii\gii\Generator const RELATIONS_ALL_INVERSE = 'all-inverse'; public $db = 'db'; - public $ns = 'app\models'; + public $ns = 'app\modules\moduleName\domains\domainName\activerecords'; public $tableName; public $modelClass; public $baseClass = 'yii\db\ActiveRecord'; @@ -240,13 +240,9 @@ public function generate() } $businessFile = Yii::getAlias('@' . str_replace('\\', '/', $this->getBusinessClass())) . '.php'; - $resourceFile = Yii::getAlias('@' . str_replace('\\', '/', $this->getResourceClass())) . '.php'; - $resourceTestFile = $this->getResourceUnitTestPath(); $files = array_merge($files, [ new CodeFile($businessFile, $this->render('business.php', $params)), - new CodeFile($resourceFile, $this->render('resource.php')), - new CodeFile($resourceTestFile, $this->render('../tests/resource.test.php')), ]); } @@ -878,39 +874,4 @@ public function getBusinessClass($namespaceOnly = false) return implode('\\', $parts); } - - public function getResourceClass($namespaceOnly = false) - { - $parts = StringHelper::explode($this->ns, '\\'); - // unset `activerecords` - $activeRecordsKey = array_search('activerecords', $parts); - unset($parts[$activeRecordsKey]); - $parts[] = $this->modelClass . 'Resource'; - - if ($namespaceOnly) { - array_pop($parts); - } - - return implode('\\', $parts); - } - - public function getResourceUnitTestPath() - { - $path = Yii::getAlias('@' . str_replace('\\', '/', $this->ns)); - $parts = StringHelper::explode($path, '/'); - // replace `app` to `test` - $resourceKey = array_search('app', $parts); - $parts[$resourceKey] = 'app\\tests\\codeception\\unit'; - // unset `activerecords` - $activeRecordsKey = array_search('activerecords', $parts); - unset($parts[$activeRecordsKey]); - $parts[] = $this->modelClass . 'ResourceTest'; - - return implode('\\', $parts) . '.php'; - } - - public function getResourceUnitTestClass() - { - return $this->modelClass . 'ResourceTest'; - } } diff --git a/src/gii/generators/model/default/query.php b/src/gii/generators/model/default/query.php index 36b9d136..3c807300 100644 --- a/src/gii/generators/model/default/query.php +++ b/src/gii/generators/model/default/query.php @@ -33,7 +33,7 @@ class extends queryBaseClass, '\\ { /*public function active() { - return $this->andWhere('[[status]]=1'); + return $this->andWhere(['status' => 1]); }*/ /** diff --git a/src/gii/generators/model/default/resource.php b/src/gii/generators/model/default/resource.php deleted file mode 100644 index 73962cb7..00000000 --- a/src/gii/generators/model/default/resource.php +++ /dev/null @@ -1,75 +0,0 @@ -modelClass); - -echo " - -namespace getResourceClass(), '\\')) ?>; - -use Yii; -use \albertborsos\ddd\models\AbstractResource; -use getBusinessClass() ?>; - -/** - * getResourceClass()) ?> handles the `insert`, `update` and `delete` processes for `modelClass ?>`. - */ -class getResourceClass()) ?> extends AbstractResource -{ - /** - * Business logic to store data. - * - * @return bool - */ - protected function insert() - { - $model = new (); - $model->load($this->getForm()->attributes, ''); - - if ($model->save()) { - $this->setId($model->id); - return true; - } - - $this->getForm()->addErrors($model->getErrors()); - return false; - } - - /** - * Business logic to update data. - * - * @return bool - */ - protected function update() - { - $model = $this->getModel(); - $model->load($this->getForm()->attributes, ''); - - if ($model->save()) { - return true; - } - - $this->getForm()->addErrors($model->getErrors()); - return false; - } - - /** - * Business logic to delete data. - * - * @return bool - */ - public function delete() - { - $this->getModel()->delete(); - return true; - } -} diff --git a/src/gii/generators/model/tests/resource.test.php b/src/gii/generators/model/tests/resource.test.php deleted file mode 100644 index 6060bd57..00000000 --- a/src/gii/generators/model/tests/resource.test.php +++ /dev/null @@ -1,49 +0,0 @@ -modelClass); - -echo " - -class getResourceUnitTestClass()) ?> extends AbstractBaseResourceTest -{ - protected $modelClass = 'getBusinessClass() ?>'; - protected $resourceClass = 'getResourceClass() ?>'; - - public function fixtures() - { - return [ - '' => \tests\codeception\unit\fixtures\Fixture::className(), - ]; - } - - public function deleteProvider() - { - return [ - 'delete active ' => ['1'], - 'delete inactive ' => ['2'], - 'delete soft deleted ' => ['3'], - ]; - } - - /** - * @skip - * @dataProvider deleteProvider - */ - public function testDelete($fixtureAlias) - { - $model = $this->getModel($this->[$fixtureAlias]['id']); - - $resource = $this->mockResource(null, $model); - $resource->delete(); - - $model = $this->getModel($this->[$fixtureAlias]['id']); - $this->assertNull($model); - } -} diff --git a/src/grid/DataColumn.php b/src/grid/DataColumn.php index a6c7a2f3..90ae1a3a 100644 --- a/src/grid/DataColumn.php +++ b/src/grid/DataColumn.php @@ -5,6 +5,11 @@ use albertborsos\ddd\data\ResourceDataProvider; use yii\helpers\ArrayHelper; +/** + * Class DataColumn + * @deprecated since 0.3.0, will be removed in 1.0.0. + * @package albertborsos\ddd\grid + */ class DataColumn extends \yii\grid\DataColumn { private $_isResourceProvider = false; diff --git a/src/grid/GridView.php b/src/grid/GridView.php index 637ef157..77a22129 100644 --- a/src/grid/GridView.php +++ b/src/grid/GridView.php @@ -2,6 +2,11 @@ namespace albertborsos\ddd\grid; +/** + * Class GridView + * @deprecated since 0.3.0, will be removed in 1.0.0. + * @package albertborsos\ddd\grid + */ class GridView extends \yii\grid\GridView { public $dataColumnClass = 'app\components\grid\DataColumn'; diff --git a/src/models/AbstractDomain.php b/src/models/AbstractDomain.php index 288b45f0..91c226e5 100644 --- a/src/models/AbstractDomain.php +++ b/src/models/AbstractDomain.php @@ -9,13 +9,12 @@ /** * Class AbstractDomain * @package albertborsos\ddd\models + * @deprecated since 0.3.0, will be removed in 1.0.0. Use `AbstractService` instead. */ abstract class AbstractDomain extends AbstractModel { /** - * Business logic to store data for multiple resources. - * - * @return mixed + * @return boolean */ abstract public function process(); diff --git a/src/models/AbstractResource.php b/src/models/AbstractResource.php index 15690370..71057dbc 100644 --- a/src/models/AbstractResource.php +++ b/src/models/AbstractResource.php @@ -11,6 +11,7 @@ /** * Class AbstractResource + * @deprecated since 0.3.0, will be removed in 1.0.0. * @package albertborsos\ddd\models */ abstract class AbstractResource extends AbstractModel diff --git a/src/models/AbstractService.php b/src/models/AbstractService.php new file mode 100644 index 00000000..776a8a8d --- /dev/null +++ b/src/models/AbstractService.php @@ -0,0 +1,57 @@ +getForm()->getAttributes(); + } + + /** + * Converts the model into an array. + * + * This method will first identify which fields to be included in the resulting array by calling [[resolveFields()]]. + * It will then turn the model into an array with these fields. If `$recursive` is true, + * any embedded objects will also be converted into arrays. + * + * If the model implements the [[Linkable]] interface, the resulting array will also have a `_link` element + * which refers to a list of links as specified by the interface. + * + * @param array $fields the fields being requested. If empty, all fields as specified by [[fields()]] will be returned. + * @param array $expand the additional fields being requested for exporting. Only fields declared in [[extraFields()]] + * will be considered. + * @param bool $recursive whether to recursively return array representation of embedded objects. + * @return array the array representation of the object + */ + public function toArray(array $fields = [], array $expand = [], $recursive = true) + { + $data = []; + foreach ($this->resolveFields($fields, $expand) as $field => $definition) { + $data[$field] = is_string($definition) ? $this->getForm()->$definition : call_user_func($definition, $this, $field); + } + + if ($this instanceof Linkable) { + $data['_links'] = Link::serialize($this->getLinks()); + } + + return $recursive ? ArrayHelper::toArray($data) : $data; + } +} diff --git a/tests/_support/base/AbstractBaseResourceTest.php b/tests/_support/base/AbstractBaseResourceTest.php index f55bf982..65d61428 100644 --- a/tests/_support/base/AbstractBaseResourceTest.php +++ b/tests/_support/base/AbstractBaseResourceTest.php @@ -5,6 +5,11 @@ use Codeception\Test\Unit; use Yii; +/** + * Class AbstractBaseResourceTest + * @deprecated since 0.3.0, will be removed in 1.0.0. + * @package albertborsos\ddd\tests\support\base + */ abstract class AbstractBaseResourceTest extends Unit { protected $resourceClass; diff --git a/tests/_support/base/AbstractDomainTest.php b/tests/_support/base/AbstractDomainTest.php index dd848833..c0d27285 100644 --- a/tests/_support/base/AbstractDomainTest.php +++ b/tests/_support/base/AbstractDomainTest.php @@ -5,6 +5,11 @@ use Codeception\Test\Unit; use Yii; +/** + * Class AbstractDomainTest + * @deprecated since 0.3.0, will be removed in 1.0.0. Use `albertborsos\ddd\tests\support\base\AbstractServiceTest` instead. + * @package albertborsos\ddd\tests\support\base + */ abstract class AbstractDomainTest extends Unit { protected $formClass; diff --git a/tests/_support/base/AbstractServiceTest.php b/tests/_support/base/AbstractServiceTest.php new file mode 100644 index 00000000..f143058f --- /dev/null +++ b/tests/_support/base/AbstractServiceTest.php @@ -0,0 +1,53 @@ +formClass, $params); + $form->load($loadParams, ''); + + return $form; + } + + /** + * @param \albertborsos\ddd\interfaces\FormObject $formObject + * @param \albertborsos\ddd\interfaces\BusinessObject|null $businessObject + * @return \albertborsos\ddd\models\AbstractService + */ + protected function mockService(\albertborsos\ddd\interfaces\FormObject $formObject, \albertborsos\ddd\interfaces\BusinessObject $businessObject = null) + { + return Yii::createObject($this->serviceClass, [$formObject, $businessObject]); + } + + /** + * @param $id + * @return \yii\db\ActiveRecord + */ + protected function getModel($id) + { + return call_user_func([$this->modelClass, 'findOne'], $id); + } +}