├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── codeception.dist.yml ├── composer.json ├── diagram.png ├── docs ├── guide-es │ ├── README.md │ └── data-type.php └── guide │ └── data-type.php ├── src ├── behaviors │ └── Positionable.php ├── fixtures │ ├── DataTypeFixture.php │ └── data │ │ └── data_type.php ├── migrations │ └── tables │ │ ├── m170101_000001_form.php │ │ ├── m170101_000002_data_type.php │ │ ├── m170101_000003_field.php │ │ ├── m170101_000004_field_rule.php │ │ ├── m170101_000005_field_rule_property.php │ │ ├── m170101_000006_form_section.php │ │ ├── m170101_000007_form_section_field.php │ │ ├── m170101_000008_form_solicitude.php │ │ └── m170101_000009_form_solicitude_value.php ├── models │ ├── DataType.php │ ├── Field.php │ ├── FieldRule.php │ ├── FieldRuleProperty.php │ ├── Form.php │ ├── Section.php │ ├── SectionField.php │ ├── Solicitude.php │ └── SolicitudeValue.php └── roa │ ├── models │ ├── DataType.php │ ├── DataTypeSearch.php │ ├── Field.php │ ├── FieldRule.php │ ├── FieldRuleProperty.php │ ├── FieldRulePropertySearch.php │ ├── FieldSearch.php │ ├── Form.php │ ├── FormSearch.php │ ├── Section.php │ ├── SectionField.php │ ├── SectionFieldSearch.php │ ├── SectionSearch.php │ ├── Solicitude.php │ ├── SolicitudeSearch.php │ ├── SolicitudeValue.php │ ├── SolicitudeValueSearch.php │ └── SolicitudeValueSimpleSearch.php │ ├── modules │ └── Version.php │ └── resources │ ├── DataTypeResource.php │ ├── FieldResource.php │ ├── FormResource.php │ ├── SolicitudeValueResource.php │ ├── field │ ├── RuleResource.php │ └── rule │ │ └── PropertyResource.php │ └── form │ ├── SectionResource.php │ ├── SolicitudeResource.php │ ├── section │ └── FieldResource.php │ └── solicitude │ └── ValueResource.php └── tests ├── _app ├── assets │ └── .gitignore ├── config │ ├── .gitignore │ ├── common.php │ ├── console.php │ ├── db.php │ ├── test.php │ └── web.php ├── controllers │ └── SiteController.php ├── fixtures │ ├── FieldFixture.php │ ├── FieldRuleFixture.php │ ├── FieldRulePropertyFixture.php │ ├── FormFixture.php │ ├── OauthAccessTokensFixture.php │ ├── OauthClientsFixture.php │ ├── SectionFieldFixture.php │ ├── SectionFixture.php │ ├── SolicitudeFixture.php │ ├── SolicitudeValueFixture.php │ ├── UserFixture.php │ └── data │ │ ├── access_tokens.php │ │ ├── clients.php │ │ ├── field.php │ │ ├── field_rule.php │ │ ├── field_rule_property.php │ │ ├── form.php │ │ ├── section.php │ │ ├── section_field.php │ │ ├── solicitude.php │ │ ├── solicitude_value.php │ │ └── user.php ├── index.php ├── migrations │ └── m130101_000001_user.php ├── models │ └── User.php ├── views │ ├── layouts │ │ └── main.php │ └── site │ │ └── index.php └── yii.php ├── _bootstrap.php ├── _data └── .gitkeep ├── _output └── .gitignore ├── _support ├── ApiTester.php ├── FunctionalTester.php ├── Helper │ ├── Acceptance.php │ ├── Api.php │ ├── Functional.php │ └── Unit.php └── UnitTester.php ├── api.suite.yml ├── api ├── DataTypeCest.php ├── FieldCest.php ├── FormCest.php ├── SearchValueCest.php ├── _bootstrap.php ├── field │ ├── FieldRuleCest.php │ └── rule │ │ └── FieldRulePropertyCest.php └── form │ ├── SectionCest.php │ ├── SolicitudeCest.php │ ├── section │ └── SectionFieldCest.php │ └── solicitude │ └── SolicitudeValueCest.php ├── unit.suite.yml └── unit ├── _bootstrap.php └── behaviors └── PositionableCest.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Folders to ignore 2 | vendor 3 | 4 | # autogenerated files 5 | composer.lock 6 | tests/_support/_generated 7 | tests/_app/runtime 8 | 9 | 10 | # OS or Editor files and folders 11 | .buildpath 12 | .cache 13 | .DS_Store 14 | .idea 15 | .project 16 | .settings 17 | .tmproj 18 | desktop.ini 19 | nbproject 20 | Thumbs.db 21 | *.sublime-project 22 | *.sublime-workspace 23 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: [tests/_support/_generated/*] 3 | checks: 4 | php: 5 | code_rating: true 6 | verify_property_names: false 7 | use_statement_alias_conflict: true 8 | use_self_instead_of_fqcn: true 9 | uppercase_constants: true 10 | unused_variables: true 11 | unused_properties: true 12 | unused_parameters: false 13 | unused_methods: true 14 | unreachable_code: true 15 | too_many_arguments: true 16 | sql_injection_vulnerabilities: true 17 | return_doc_comments: true 18 | return_doc_comment_if_not_inferrable: true 19 | require_scope_for_properties: true 20 | require_scope_for_methods: true 21 | require_php_tag_first: true 22 | psr2_switch_declaration: true 23 | psr2_class_declaration: true 24 | property_assignments: true 25 | properties_in_camelcaps: true 26 | prefer_while_loop_over_for_loop: true 27 | precedence_mistakes: true 28 | precedence_in_conditions: true 29 | php5_style_constructor: true 30 | parse_doc_comments: true 31 | parameters_in_camelcaps: false 32 | parameter_non_unique: true 33 | parameter_doc_comments: true 34 | param_doc_comment_if_not_inferrable: true 35 | overriding_private_members: true 36 | overriding_parameter: true 37 | optional_parameters_at_the_end: true 38 | one_class_per_file: true 39 | non_commented_empty_catch_block: true 40 | no_unnecessary_if: true 41 | no_unnecessary_final_modifier: true 42 | no_underscore_prefix_in_properties: true 43 | no_underscore_prefix_in_methods: true 44 | no_trait_type_hints: true 45 | no_trailing_whitespace: true 46 | no_short_open_tag: true 47 | no_property_on_interface: true 48 | no_non_implemented_abstract_methods: true 49 | no_goto: true 50 | no_global_keyword: true 51 | no_exit: true 52 | no_eval: true 53 | no_error_suppression: true 54 | no_empty_statements: true 55 | no_duplicate_arguments: true 56 | no_debug_code: true 57 | no_commented_out_code: true 58 | more_specific_types_in_doc_comments: true 59 | missing_arguments: true 60 | method_calls_on_non_object: true 61 | line_length: 62 | max_length: '120' 63 | instanceof_class_exists: true 64 | function_in_camel_caps: true 65 | foreach_usable_as_reference: true 66 | foreach_traversable: true 67 | encourage_single_quotes: true 68 | encourage_shallow_comparison: true 69 | encourage_postdec_operator: true 70 | duplication: true 71 | deprecated_code_usage: true 72 | deadlock_detection_in_loops: true 73 | comparison_always_same_result: true 74 | code_rating: true 75 | closure_use_not_conflicting: true 76 | closure_use_modifiable: true 77 | classes_in_camel_caps: true 78 | check_method_contracts: 79 | verify_interface_like_constraints: true 80 | verify_documented_constraints: true 81 | verify_parent_constraints: true 82 | catch_class_exists: true 83 | call_to_parent_method: true 84 | blank_line_after_namespace_declaration: true 85 | avoid_useless_overridden_methods: true 86 | avoid_usage_of_logical_operators: true 87 | avoid_unnecessary_concatenation: true 88 | avoid_todo_comments: true 89 | avoid_superglobals: true 90 | avoid_perl_style_comments: true 91 | avoid_multiple_statements_on_same_line: true 92 | avoid_length_functions_in_loops: true 93 | avoid_fixme_comments: true 94 | avoid_duplicate_types: true 95 | avoid_corrupting_byteorder_marks: true 96 | avoid_conflicting_incrementers: true 97 | avoid_closing_tag: true 98 | assignment_of_null_return: true 99 | argument_type_checks: true 100 | align_assignments: true 101 | remove_extra_empty_lines: true 102 | remove_php_closing_tag: true 103 | remove_trailing_whitespace: true 104 | fix_use_statements: 105 | remove_unused: true 106 | preserve_multiple: false 107 | preserve_blanklines: true 108 | order_alphabetically: true 109 | fix_php_opening_tag: true 110 | fix_linefeed: true 111 | fix_line_ending: true 112 | fix_identation_4spaces: true 113 | fix_doc_comments: true 114 | 115 | coding_style: 116 | php: 117 | spaces: 118 | before_parentheses: 119 | closure_definition: true 120 | around_operators: 121 | concatenation: true 122 | other: 123 | after_type_cast: false 124 | braces: 125 | classes_functions: 126 | class: new-line 127 | function: new-line 128 | closure: end-of-line 129 | if: 130 | opening: end-of-line 131 | for: 132 | opening: end-of-line 133 | while: 134 | opening: end-of-line 135 | do_while: 136 | opening: end-of-line 137 | switch: 138 | opening: end-of-line 139 | try: 140 | opening: end-of-line 141 | upper_lower_casing: 142 | constants: 143 | true_false_null: lower 144 | tools: 145 | external_code_coverage: false 146 | php_analyzer: true 147 | php_code_coverage: true 148 | php_code_sniffer: 149 | config: 150 | standard: PSR2 151 | sniffs: { generic: { formatting: { multiple_statement_alignment_sniff: false } } } 152 | php_cpd: 153 | enabled: false # solicitado por scrutinizer para usar php_analyzer 154 | excluded_dirs: [vendor, tests] 155 | build: 156 | environment: 157 | php: 158 | version: 7.1 159 | nodes: 160 | my-tests: 161 | environment: 162 | mysql: 5.6 163 | project_setup: 164 | before: 165 | # crear la base de datos, el nombre tiene que coincidir con la base 166 | # del archivo `environments/dev/common/config/main-local.php` 167 | - mysql -e "create database yii2_formgenerator_test" 168 | tests: 169 | before: 170 | # run migrations 171 | - composer deploy-tests 172 | override: 173 | # test coverage 174 | - 175 | # comando de coverage 176 | command: composer run-coverage 177 | coverage: 178 | file: 'tests/_output/coverage.xml' 179 | format: 'clover' 180 | idle_timeout: 1800 181 | analysis: 182 | project_setup: 183 | override: [true] 184 | 185 | tests: 186 | override: 187 | - php-scrutinizer-run -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # faster builds on new travis setup not using sudo 2 | sudo: false 3 | 4 | language: php 5 | 6 | services: 7 | - memcached 8 | - mysql 9 | - postgresql 10 | 11 | php: 12 | - 7.1 13 | - 7.2 14 | - 7.3 15 | - nightly 16 | 17 | matrix: 18 | fast_finish: true 19 | allow_failures: 20 | - php: nightly 21 | 22 | install: 23 | - travis_retry composer self-update 24 | - travis_retry composer install --prefer-dist --no-interaction 25 | 26 | before_script: 27 | - php -r "echo INTL_ICU_VERSION . \"\n\";" 28 | - php -r "echo INTL_ICU_DATA_VERSION . \"\n\";" 29 | - mysql --version 30 | 31 | # initialize database 32 | - mysql -e "create database yii2_formgenerator_test" 33 | 34 | script: 35 | - composer deploy-tests 36 | - composer run-tests 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Yii2 Form Generator Change Log 2 | ========================== 3 | 4 | 0.5.0 July 13, 2019 5 | 6 | - Brk: Methods now use the typecast supported in php 7.1 7 | 8 | 0.4.2 December 19, 2018 9 | 10 | - Require flowpath on dev 11 | 12 | 0.4.1 November 15, 2018 13 | ------------------------ 14 | 15 | - Enh: Extrafields, form and section 16 | 17 | 0.4.0 October 18, 2018 18 | ------------------------ 19 | 20 | - Enh: Use Cases to use Yii2 Formgenerator 21 | - Brk: Upgraded requirement to Yii2 ROA 0.4.1 22 | - Brk: ROA resources now extend from 23 | `tecnocen\roa\controllers\Resource` 24 | - Enh: ROA Models implement `tecnocen\roa\hal\Contract` 25 | 26 | 0.3.0 March 3, 2018 27 | ------------------------ 28 | 29 | - Enh: Live Demo 30 | 31 | 0.2.0 February 3, 2018 32 | ------------------------ 33 | 34 | - Enh: #12 Add tests 35 | 36 | 0.1.3 November 8, 2017 37 | ------------------------ 38 | 39 | - Bug: Class yii\helpers\Url where is used 40 | 41 | 0.1.2 November 2, 2017 42 | ------------------------ 43 | 44 | - Bug: Error typo migration form_section 45 | - Bug: Change column label for table form_section_field 46 | 47 | 0.1.1 November 2, 2017 48 | ------------------------ 49 | 50 | - Bug: Adding data in fixtures for table data_type 51 | - Bug: Adding data in fixtures for table field 52 | - Bug: Adding data in fixtures for table form_section_field 53 | 54 | 0.1.0 October 30, 2017 55 | ----------------------------- 56 | 57 | - Initial release. 58 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | This code of conduct outlines our expectations for participants within the **Tecnocen.com - Open Source** community, as well as steps to reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community. 3 | 4 | Our open source community strives to: 5 | 6 | * **Be friendly and patient.** 7 | * **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. 8 | * **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language. 9 | * **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. 10 | * **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. 11 | * **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. 12 | 13 | ## Definitions 14 | 15 | Harassment includes, but is not limited to: 16 | 17 | - Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, age, regional discrimination, political or religious affiliation 18 | - Unwelcome comments regarding a person’s lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment 19 | - Deliberate misgendering. This includes deadnaming or persistently using a pronoun that does not correctly reflect a person's gender identity. You must address people by the name they give you when not addressing them by their username or handle 20 | - Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop 21 | - Threats of violence, both physical and psychological 22 | - Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm 23 | - Deliberate intimidation 24 | - Stalking or following 25 | - Harassing photography or recording, including logging online activity for harassment purposes 26 | - Sustained disruption of discussion 27 | - Unwelcome sexual attention, including gratuitous or off-topic sexual images or behaviour 28 | - Pattern of inappropriate social contact, such as requesting/assuming inappropriate levels of intimacy with others 29 | - Continued one-on-one communication after requests to cease 30 | - Deliberate “outing” of any aspect of a person’s identity without their consent except as necessary to protect others from intentional abuse 31 | - Publication of non-harassing private communication 32 | 33 | Our open source community prioritizes marginalized people’s safety over privileged people’s comfort. We will not act on complaints regarding: 34 | 35 | - ‘Reverse’ -isms, including ‘reverse racism,’ ‘reverse sexism,’ and ‘cisphobia’ 36 | - Reasonable communication of boundaries, such as “leave me alone,” “go away,” or “I’m not discussing this with you” 37 | - Refusal to explain or debate social justice concepts 38 | - Communicating in a ‘tone’ you don’t find congenial 39 | - Criticizing racist, sexist, cissexist, or otherwise oppressive behavior or assumptions 40 | 41 | 42 | ### Diversity Statement 43 | 44 | We encourage everyone to participate and are committed to building a community for all. Although we will fail at times, we seek to treat everyone both as fairly and equally as possible. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong. 45 | 46 | Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected 47 | characteristics above, including participants with disabilities. 48 | 49 | ### Reporting Issues 50 | 51 | If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via **opensource@tecnocen.com**. All reports will be handled with discretion. In your report please include: 52 | 53 | - Your contact information. 54 | - Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please 55 | include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link. 56 | - Any additional information that may be helpful. 57 | 58 | After filing a report, a representative will contact you personally, review the incident, follow up with any additional questions, and make a decision as to how to respond. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. If the complaint originates from a member of the response team, it will be handled by a different member of the response team. We will respect confidentiality requests for the purpose of protecting victims of abuse. 59 | 60 | Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the representative may take any action they deem appropriate, up to and including a permanent ban from our community without warning. 61 | 62 | This Code Of Conduct follows the [template](http://todogroup.org/opencodeofconduct/) established by the [TODO Group](http://todogroup.org). 63 | 64 | ### Attribution & Acknowledgements 65 | 66 | We all stand on the shoulders of giants across many open source communities. We'd like to thank the communities and projects that established code of conducts and diversity statements as our inspiration: 67 | 68 | * [Django](https://www.djangoproject.com/conduct/reporting/) 69 | * [Python](https://www.python.org/community/diversity/) 70 | * [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct) 71 | * [Contributor Covenant](http://contributor-covenant.org/) 72 | * [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/) 73 | * [Citizen Code of Conduct](http://citizencodeofconduct.org/) 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to Tecnocen Yii2 Form Generator 2 | ====================================== 3 | 4 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: 5 | 6 | The following is a set of guidelines for contributing to Tecnocen and its 7 | packages, which are hosted in the 8 | [Tecnocen Organization](https://github.com/tecnocen-com) on GitHub. These 9 | are mostly guidelines, not rules. Use your best judgment, and feel free to 10 | propose changes to this document in a pull request. 11 | 12 | Forums and Support 13 | ------------------ 14 | 15 | We are currently piggy backing from 16 | [Yii2 Extensions Forum](http://www.yiiframework.com/forum/index.php/forum/13-extensions/) 17 | you can make questions there where other Yii2 devs might see it and answer it. 18 | 19 | If you need profesional support please feel free to contact support@tecnocen.com with the following information. 20 | 21 | - What steps will reproduce the problem? 22 | - What is the expected result? 23 | - What do you get instead? 24 | - Additional info 25 | 26 | 27 | Reporting Bugs 28 | -------------- 29 | 30 | Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). 31 | Its required for you to follow this steps when submitting a bug: 32 | 33 | 1. Determine its not already reported by searching existing issues 34 | https://github.com/search?q=+is%3Aissue+user%3Atecnocen-com 35 | 36 | 2. Determine its not an issue of any of the dependencies of this library such as 37 | [Yii2](https://github.com/search?q=+is%3Aissue+user%3Ayiisoft) 38 | 39 | 3. If the bug has not been reported then provide enough information to reproduce 40 | and isolate the bug. 41 | - Use a descriptive title to differentiate the bug. 42 | - Describe the exact steps to reproduce the issue. Focus on how you did it 43 | instead of what you did. 44 | - Provide specific examples to demonstrate the steps. You can upload files, 45 | screen captures, links to copy/paste snippets, etc. 46 | - Describe observed behavior. 47 | - Describe expected behavior. 48 | - If you report a server error caused by the library include the error 49 | message you get along with the exception trace if you have it. 50 | - Provide the versions used by composer using the comand 51 | > composer show 52 | 53 | 4. Stay in touch, check the issue for feedback from other users and developers. 54 | 55 | Suggesting Enhancements 56 | ----------------------- 57 | 58 | You can suggest enhancements for the library using Github Issues aswell. Its 59 | required for you to follow this steps when suggesting a change. 60 | 61 | 1. Read the documentation and see if the enhancement does not exists already. 62 | 63 | 2. Check the description of the library and see if there are use cases for your 64 | enhacement into the library. 65 | 66 | 3. Check if there is not a package already which covers the use case described 67 | on the previous step. 68 | 69 | 4. Check it was not already suggested 70 | https://github.com/search?q=+is%3Aissue+user%3Atecnocen-com 71 | 72 | 5. If the enhancement was not suggested already then provide enough information 73 | to understand and develop the enhacenment 74 | - Use a descriptive title to differentiate the enhancement. 75 | - Describe the exact steps to utilize the enhancement. Focus on how you plant 76 | to use it and implement instead on what you want implemented. 77 | - Provide specific examples to demonstrate the steps. You can upload files, 78 | screen captures, links to copy/paste snippets, etc. 79 | - Describe expected behavior. 80 | 81 | Code Contribution 82 | ----------------- 83 | 84 | Code contributions are handled by Github Pull Requests. 85 | 86 | ### Testing Environment 87 | 88 | This library contains tools to set up a testing environment using composer 89 | scripts. 90 | P 91 | - Clone the repository from github and move to the created folder. 92 | - edit or create the file `tests/_app/config/db.local.php` with your db 93 | credentials 94 | - Prepare your local environment to run the tests with composer script 95 | > composer deploy-tests 96 | - When the command finish executing successfully you can run 97 | `composer run-tests` to run all the codeception tests of the library. 98 | 99 | You can now write changes locally and run tests to change 100 | 101 | Creating Pull Requests 102 | ---------------------- 103 | 104 | When you are ready to submit your contribution you can create a pull request. 105 | Its required for you to follow this steps when creating a pull request. 106 | 107 | 1. Use a descriptive title, include the issues and pull request #id the new 108 | pull request affects on the tile. 109 | 2. Describe the use case or bug it covers, keep it simple by focusing on one 110 | functionality at once. 111 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## **What steps will reproduce the problem?** 2 | 3 | Description 4 | 5 | ``` 6 | Add code here if u need 7 | ``` 8 | 9 | ## **What is the expected result?** 10 | 11 | Description 12 | 13 | ``` 14 | Add code here if u need 15 | ``` 16 | 17 | ## **What do you get instead?** 18 | 19 | Description 20 | 21 | ``` 22 | Add code here if u need 23 | ``` 24 | 25 | 26 | ## **Additional info** 27 | 28 | Description 29 | 30 | Table example if u need 31 | 32 | Title1 | Title2 | Title3| 33 | -- | -- | -- 34 | data1 | data2 | data3 35 | data n + 1 | data n + 2 | data n + 3 36 | 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present Tecnocen.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | | Q | A 2 | | ------------- | --- 3 | | Is bugfix? | yes/no 4 | | New feature? | yes/no 5 | | Breaks BC? | yes/no 6 | | Tests pass? | yes/no 7 | | Fixed issues | comma-separated list of tickets # fixed by the PR, if any 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tecnocen Form Generator 2 | ======================= 3 | 4 | Library to dynamically generate forms in database. 5 | 6 | [![Latest Stable Version](https://poser.pugx.org/tecnocen/yii2-formgenerator/v/stable)](https://packagist.org/packages/tecnocen/yii2-formgenerator) 7 | [![Total Downloads](https://poser.pugx.org/tecnocen/yii2-formgenerator/downloads)](https://packagist.org/packages/tecnocen/yii2-formgenerator) 8 | 9 | 10 | Travis [![Build Status Travis](https://travis-ci.org/tecnocen-com/yii2-formgenerator.svg?branch=master&style=flat?style=for-the-badge)](https://travis-ci.org/tecnocen-com/yii2-formgenerator) 11 | 12 | ## Getting Started 13 | 14 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. 15 | 16 | ### Prerequisites 17 | 18 | - Install PHP 7.1 or higher 19 | - [Composer Installed](https://getcomposer.org/doc/00-intro.md) 20 | 21 | The rest of the requirements are checked by composer when installing the 22 | repository on the next step. 23 | 24 | ### Installation 25 | ---------------- 26 | 27 | You can use composer to install the library `tecnocen/yii2-formgenerator` by running 28 | the command; 29 | 30 | `composer require tecnocen/yii2-formgenerator` 31 | 32 | or edit the `composer.json` file 33 | 34 | ```json 35 | require: { 36 | "tecnocen/yii2-formgenerator": "*", 37 | } 38 | ``` 39 | 40 | ### Deployment 41 | 42 | Then run the required migrations 43 | 44 | `php yii migrate/up -p=@tecnocen/formgenerator/migrations/tables` 45 | 46 | Which will install the following table structure 47 | 48 | ![Database Diagram](diagram.png) 49 | 50 | 51 | #### ROA Backend Usage 52 | ----------------- 53 | 54 | The ROA support is very simple and can be done by just adding a module version 55 | to the api container which will be used to hold the resources. 56 | 57 | ```php 58 | class Api extends \tecnocen\roa\modules\ApiContainer 59 | { 60 | $versions = [ 61 | // other versions 62 | 'fg1' => ['class' => 'tecnocen\formgenerator\roa\modules\Version'], 63 | ]; 64 | } 65 | ``` 66 | 67 | You can then access the module to check the available resources. 68 | 69 | - data-type 70 | - field 71 | - field//rule 72 | - field//rule//property 73 | - form 74 | - form//section 75 | - form//section//field 76 | - form//solicitude 77 | - form//solicitude//value 78 | 79 | Which will implement CRUD functionalities for a formgenerator. 80 | 81 | Alternatively you can add the resource routes to your existing version. 82 | 83 | ## Running the tests 84 | 85 | This library contains tools to set up a testing environment using composer scripts, for more information see [Testing Environment](https://github.com/tecnocen-com/yii2-formgenerator/blob/master/CONTRIBUTING.md) section. 86 | 87 | ### Break down into end to end tests 88 | 89 | Once testing environment is setup, run the following commands. 90 | 91 | ``` 92 | composer deploy-tests 93 | ``` 94 | 95 | Run tests. 96 | 97 | ``` 98 | composer run-tests 99 | ``` 100 | 101 | Run tests with coverage. 102 | 103 | ``` 104 | composer run-coverage 105 | ``` 106 | 107 | ## Live Demo 108 | 109 | You can run a live demo on a freshly installed project to help you run testing 110 | or understand the responses returned by the server. The live demo is initialized 111 | with the command. 112 | 113 | ``` 114 | php -S localhost:8000 -t tests/_app 115 | ``` 116 | 117 | Where `:8000` is the port number which can be changed. This allows you call ROA 118 | services on a browser or REST client. 119 | 120 | ## Use Cases 121 | 122 | TO DO 123 | 124 | ## Built With 125 | 126 | * Yii 2: The Fast, Secure and Professional PHP Framework [http://www.yiiframework.com](http://www.yiiframework.com) 127 | 128 | ## Code of Conduct 129 | 130 | Please read [CODE_OF_CONDUCT.md](https://github.com/tecnocen-com/yii2-formgenerator/blob/master/CODE_OF_CONDUCT.md) for details on our code of conduct. 131 | 132 | ## Contributing 133 | 134 | Please read [CONTRIBUTING.md](https://github.com/tecnocen-com/yii2-formgenerator/blob/master/CONTRIBUTING.md) for details on the process for submitting pull requests to us. 135 | 136 | ## Versioning 137 | 138 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/tecnocen-com/yii2-formgenerator/tags). 139 | 140 | _Considering [SemVer](http://semver.org/) for versioning rules 9, 10 and 11 talk about pre-releases, they will not be used within the Tecnocen-com._ 141 | 142 | ## Authors 143 | 144 | * [**Angel Guevara**](https://github.com/Faryshta) - *Initial work* - [Tecnocen.com](https://github.com/Tecnocen-com) 145 | * [**Carlos Llamosas**](https://github.com/neverabe) - *Initial work* - [Tecnocen.com](https://github.com/Tecnocen-com) 146 | 147 | See also the list of [contributors](https://github.com/tecnocen-com/yii2-formgenerator/graphs/contributors) who participated in this project. 148 | 149 | ## License 150 | 151 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 152 | 153 | ## Acknowledgments 154 | 155 | * TO DO - Hat tip to anyone who's code was used 156 | * TO DO - Inspiration 157 | * TO DO - etc 158 | 159 | [![yii2-workflow](https://img.shields.io/badge/Powered__by-Tecnocen.com-orange.svg?style=for-the-badge)](https://www.tecnocen.com/) 160 | -------------------------------------------------------------------------------- /codeception.dist.yml: -------------------------------------------------------------------------------- 1 | actor: Tester 2 | paths: 3 | tests: tests 4 | log: tests/_output 5 | data: tests/_data 6 | helpers: tests/_support 7 | settings: 8 | bootstrap: _bootstrap.php 9 | colors: true 10 | memory_limit: 1024M 11 | modules: 12 | config: 13 | Yii2: 14 | configFile: 'tests/_app/config/test.php' 15 | cleanup: false 16 | coverage: 17 | enabled: true 18 | whitelist: 19 | include: 20 | - src/behaviors/* 21 | - src/models/* 22 | - src/roa/* 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tecnocen/yii2-formgenerator", 3 | "description": "Yii 2 Library to configure form generator", 4 | "keywords": [ 5 | "yii2", 6 | "framework", 7 | "form", 8 | "roa", 9 | "generator" 10 | ], 11 | "type": "yii2-extension", 12 | "license": "MIT", 13 | "minimum-stability": "dev", 14 | "authors": [ 15 | { 16 | "name": "Angel (Faryshta) Guevara", 17 | "email": "aguevara@solmipro.com", 18 | "homepage": "http://tecnocen.com" 19 | }, 20 | { 21 | "name": "Carlos (neverabe) Llamosas", 22 | "email": "carlos@tecnocen.com", 23 | "homepage": "http://tecnocen.com" 24 | } 25 | ], 26 | "repositories": [ 27 | { 28 | "type": "composer", 29 | "url": "https://asset-packagist.org" 30 | } 31 | ], 32 | "require": { 33 | "php": "^7.1", 34 | "tecnocen/yii2-roa": "~0.5.0", 35 | "tecnocen/yii2-rmdb": "*", 36 | "yiisoft/yii2": "~2.0.21" 37 | }, 38 | "require-dev": { 39 | "codeception/base": "^2.2.11", 40 | "codeception/verify": "~1.0.0", 41 | "flow/jsonpath": "~0.3", 42 | "phpunit/php-code-coverage": "5.3.*", 43 | "yiisoft/yii2-debug": "*" 44 | }, 45 | "scripts": { 46 | "deploy-tests": [ 47 | "@composer update --prefer-stable", 48 | "@php tests/_app/yii.php migrate -p=@app/migrations/ --interactive=0", 49 | "@php tests/_app/yii.php migrate -p=@tecnocen/oauth2server/migrations/tables --interactive=0", 50 | "@php tests/_app/yii.php migrate -p=@tecnocen/formgenerator/migrations/tables --interactive=0" 51 | ], 52 | "run-tests": [ 53 | "@php vendor/bin/codecept run --steps" 54 | ], 55 | "run-coverage": [ 56 | "@php vendor/bin/codecept run --steps --coverage --coverage-xml" 57 | ] 58 | }, 59 | "autoload": { 60 | "psr-4": { 61 | "tecnocen\\formgenerator\\": "src/" 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecnocen-com/yii2-formgenerator/799d814db48b3984f77762b000ade04ac619ed96/diagram.png -------------------------------------------------------------------------------- /docs/guide-es/README.md: -------------------------------------------------------------------------------- 1 | Tecnocen Form Generator 2 | ======================= 3 | 4 | Libreria para generar dinamicamente formularios usando una base de datos. 5 | 6 | [![Latest Stable Version](https://poser.pugx.org/tecnocen/yii2-formgenerator/v/stable)](https://packagist.org/packages/tecnocen/yii2-formgenerator) 7 | [![Total Downloads](https://poser.pugx.org/tecnocen/yii2-formgenerator/downloads)](https://packagist.org/packages/tecnocen/yii2-formgenerator) 8 | [![Code Coverage](https://scrutinizer-ci.com/g/tecnocen-com/yii2-formgenerator/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/tecnocen-com/yii2-formgenerator/?branch=master) 9 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/tecnocen-com/yii2-formgenerator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/tecnocen-com/yii2-formgenerator/?branch=master) 10 | 11 | Scrutinizer [![Build Status Scrutinizer](https://scrutinizer-ci.com/g/tecnocen-com/yii2-formgenerator/badges/build.png?b=master&style=flat)](https://scrutinizer-ci.com/g/tecnocen-com/yii2-formgenerator/build-status/master) 12 | Travis [![Build Status Travis](https://travis-ci.org/tecnocen-com/yii2-formgenerator.svg?branch=master&style=flat?style=for-the-badge)](https://travis-ci.org/tecnocen-com/yii2-formgenerator) 13 | 14 | ## Iniciando 15 | 16 | Estas instrucciones te generaran una copia del proyecto funcional y ejecutable 17 | en tu maquina local para propositos de despliegue y realizacion de pruebas. Vea 18 | la seccion Despliegue para las notas de como desplegar el proyecto en un sistema 19 | productivo. 20 | 21 | ### Prerequisitos 22 | 23 | - Instalar PHP 7.0 or superior 24 | - [Composer Instalado](https://getcomposer.org/doc/00-intro.md) 25 | 26 | Los demas requerimientos son revisados por composer al instalar el sistema en 27 | el proximo paso. 28 | 29 | ### Instalacion 30 | ---------------- 31 | 32 | Puedes usar composer para instalar la libreria `tecnocen/yii2-formgenerator` con 33 | el comando; 34 | 35 | `composer require tecnocen/yii2-formgenerator` 36 | 37 | o editar el archivo `composer.json` 38 | 39 | ```json 40 | require: { 41 | "tecnocen/yii2-formgenerator": "*", 42 | } 43 | ``` 44 | 45 | ### Despliegue 46 | 47 | Correr las migraciones requeridas. 48 | 49 | `php yii migrate/up -p=@tecnocen/formgenerator/migrations` 50 | 51 | Que instala la siguiente estructura. 52 | ![Database Diagram](diagram.png) 53 | 54 | #### Uso en backend con ROA 55 | --------------------------- 56 | 57 | El soporte para ROA es muy simple y puede habilitarse solo con agregar el 58 | modulo de version al contenedos de api que sera usado para contener los 59 | recursos. 60 | 61 | ```php 62 | class Api extends \tecnocen\roa\modules\ApiContainer 63 | { 64 | $versions = [ 65 | // other versions 66 | 'fg1' => ['class' => 'tecnocen\formgenerator\roa\module\Version'], 67 | ]; 68 | } 69 | ``` 70 | 71 | Despues puedes acceder al modulo para revisar los recursos disponibles. 72 | 73 | - data-type 74 | - field 75 | - field//rule 76 | - field//rule//property 77 | - form 78 | - form//section 79 | - form//section//field 80 | - form//solicitude 81 | - form//solicitude//value 82 | 83 | Que implementan funcionalidades CRUD para formgenerator. 84 | 85 | Alternativamente puedes agregar las rutas de los recursos a una version 86 | existente. 87 | 88 | ## Correr las pruebas 89 | 90 | Esta libreria contiene herramientas para habilitar entorno de pruebas usando 91 | composer scripts. Para mas informacion ver la seccion [Entorno de Pruebas](CONTRIBUTING.md) 92 | 93 | ### Despliegue de pruebas. 94 | 95 | Para desplegar el ambiente de pruebas cree o edite el archivo 96 | `tests/_app/config/db.local.php` con las configuraciones para el componente 97 | `yii\base\Application::$db` exclusivas a tu entorno local. 98 | 99 | Una vez configurado corra el comando. 100 | 101 | ``` 102 | composer deploy-tests 103 | ``` 104 | 105 | ### Correr pruebas 106 | 107 | Correr las pruebas con el comando. 108 | 109 | ``` 110 | composer run-tests 111 | ``` 112 | 113 | Correr las pruebas con cobertura de codigo. 114 | 115 | ``` 116 | composer run-coverage 117 | ``` 118 | 119 | ## Use Cases 120 | 121 | TO DO 122 | 123 | ## Built With 124 | 125 | * Yii 2: The Fast, Secure and Professional PHP Framework [http://www.yiiframework.com](http://www.yiiframework.com) 126 | 127 | ## Code of Conduct 128 | 129 | Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for details on our code of conduct. 130 | 131 | ## Contributing 132 | 133 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the process for submitting pull requests to us. 134 | 135 | ## Versioning 136 | 137 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/tecnocen-com/yii2-formgenerator/tags). 138 | 139 | _Considering [SemVer](http://semver.org/) for versioning rules 9, 10 and 11 talk about pre-releases, they will not be used within the Tecnocen-com._ 140 | 141 | ## Authors 142 | 143 | * [**Angel Guevara**](https://github.com/Faryshta) - *Initial work* - [Tecnocen.com](https://github.com/Tecnocen-com) 144 | * [**Carlos Llamosas**](https://github.com/neverabe) - *Initial work* - [Tecnocen.com](https://github.com/Tecnocen-com) 145 | 146 | See also the list of [contributors](https://github.com/tecnocen-com/yii2-formgenerator/graphs/contributors) who participated in this project. 147 | 148 | ## License 149 | 150 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 151 | 152 | ## Acknowledgments 153 | 154 | * TO DO - Hat tip to anyone who's code was used 155 | * TO DO - Inspiration 156 | * TO DO - etc 157 | 158 | [![yii2-workflow](https://img.shields.io/badge/Powered__by-Tecnocen.com-orange.svg?style=for-the-badge)](https://www.tecnocen.com/) 159 | -------------------------------------------------------------------------------- /docs/guide-es/data-type.php: -------------------------------------------------------------------------------- 1 | Data Type (Tipo de Dato) 2 | ========= 3 | 4 | Cada campo tiene un tipo de dato que ayuda a convertir los valores y 5 | organizarlos en como deben ser almacenados internamente en la base de datos. 6 | 7 | Cada registro de 'tipo de dato' contiene un name unico y un cast valido 8 | 9 | El cast debe de ser el nombre de un metodo estatico de la clase 10 | `tecnocen\formgenerator\models\DataType` o una firma del tipo 11 | `ruta\completa\Clase:metodo` donde `ruta\completa\Clase` es una clase auto 12 | cargable incluyendo namespace y `metodo` es el nombre de un metodo estatico y 13 | publico en la clase anterior. 14 | 15 | Ejemplos 16 | 17 | - `stringCast` 18 | - `app\models\DataType:json` 19 | 20 | Para habilitar los tipos de datos soportados por defectos necesitas correr el 21 | fixture `tecnocen\formgenerator\fixtures\DataTypeFixture` que proporciona Ejemplos 22 | tipos de datos. 23 | 24 | - String tipo de dato con cast como string 25 | - Integer tipo de dato con cast como integer 26 | - Decimal tipo de dato con cast como float 27 | - Boolean tipo de dato con cast como boolean 28 | - File tipo de dato con cast como yii\web\UploadedFile 29 | 30 | Puedes encontrar la lista completa de tipos de datos en el recurso `data-type`. 31 | -------------------------------------------------------------------------------- /docs/guide/data-type.php: -------------------------------------------------------------------------------- 1 | Data Type 2 | ========= 3 | 4 | Each field has a data type which helps casting values and organize them 5 | depending on how they are stored internally on the database. 6 | 7 | Each record of data type contain a unique `name` and a valid `cast`. 8 | 9 | The cast must be the name of an static method on the class 10 | `tecnocen\formgenerator\models\DataType` or a signature of the type 11 | `full\class\Name:method` where `full\class\Name` is a namespaced autoloadable 12 | class and `method` is the name of an static method on the previous class. 13 | 14 | Examples 15 | 16 | - `stringCast` 17 | - `app\models\DataType:json` 18 | 19 | To enable the default supported data types you need to run fixture 20 | `tecnocen\formgenerator\fixtures\DataTypeFixture` which provides data types 21 | 22 | - String data type with cast as string 23 | - Integer data type with cast as integer 24 | - Decimal data type with cast as float 25 | - Boolean data type with cast as boolean 26 | - File data type with cast as yii\web\UploadedFile 27 | 28 | You can find the full list of data types on the resource `data-type`. 29 | -------------------------------------------------------------------------------- /src/behaviors/Positionable.php: -------------------------------------------------------------------------------- 1 | parentAttribute) { 50 | throw new InvalidConfigException( 51 | static::class . '::$parentAttribute must be set.' 52 | ); 53 | } 54 | if (!$owner instanceof ActiveRecord) { 55 | throw new InvalidConfigException( 56 | static::class . '::$owner must extend ' . ActiveRecord::class 57 | ); 58 | } 59 | if (!$owner->hasAttribute($this->parentAttribute)) { 60 | throw new InvalidConfigException( 61 | get_class($owner) . '::$' 62 | . $this->parentAttribute 63 | . ' is not an attribute.' 64 | ); 65 | } 66 | if (!$owner->hasAttribute($this->positionAttribute)) { 67 | throw new InvalidConfigException( 68 | get_class($owner) . '::$' 69 | . $this->positionAttribute 70 | . ' is not an attribute.' 71 | ); 72 | } 73 | parent::attach($owner); 74 | $this->positionIncrease = new DbExpression( 75 | $this->positionAttribute . ' + 1' 76 | ); 77 | $this->positionDecrease = new DbExpression( 78 | $this->positionAttribute . ' - 1' 79 | ); 80 | } 81 | 82 | /** 83 | * @inheritdoc 84 | */ 85 | public function events() 86 | { 87 | return [ 88 | ActiveRecord::EVENT_BEFORE_VALIDATE => 'attachValidators', 89 | ActiveRecord::EVENT_BEFORE_INSERT => 'beforeInsert', 90 | ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate', 91 | ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete', 92 | ]; 93 | } 94 | 95 | /** 96 | * Attaches validators to the `$owner`. 97 | */ 98 | public function attachValidators() 99 | { 100 | if (!$this->attachValidators) { 101 | return; 102 | } 103 | $this->owner->validators[] = Validator::createValidator( 104 | 'default', 105 | $this->owner, 106 | $this->positionAttribute, 107 | [ 108 | 'when' => function () { 109 | return !$this->owner->hasErrors($this->parentAttribute); 110 | }, 111 | 'value' => function () { 112 | return $this->getSiblings()->max('position') + 1; 113 | } 114 | ] 115 | ); 116 | $this->owner->validators[] = Validator::createValidator( 117 | 'integer', 118 | $this->owner, 119 | $this->positionAttribute, 120 | ['min' => 1] 121 | ); 122 | } 123 | 124 | /** 125 | * @return ActiveQuery 126 | */ 127 | public function getSiblings(): ActiveQuery 128 | { 129 | return $this->owner->hasMany(get_class($this->owner), [ 130 | $this->parentAttribute => $this->parentAttribute 131 | ]); 132 | } 133 | 134 | /** 135 | * Increases the position by 1 of siblings with equal or bigger position as 136 | * the new record. 137 | */ 138 | public function beforeInsert() 139 | { 140 | $this->increaseSiblingsPosition([ 141 | '>=', 142 | $this->positionAttribute, 143 | $this->owner->getAttribute($this->positionAttribute), 144 | ]); 145 | } 146 | 147 | /** 148 | * Forbids updating `$parentAttribute` on the `$owner` record and reorganize 149 | * position among siblings when changing a record position. 150 | */ 151 | public function beforeUpdate() 152 | { 153 | if ($this->owner->isAttributeChanged($this->parentAttribute)) { 154 | throw new \yii\base\NotSupportedException( 155 | get_class($this->owner) 156 | . '::$' . $this->parentAttribute 157 | . ' is not editable.' 158 | ); 159 | } 160 | if ($this->owner->isAttributeChanged($this->positionAttribute)) { 161 | $attribute = $this->positionAttribute; 162 | $newPosition = $this->owner->getAttribute($attribute); 163 | $oldPosition = $this->owner->getOldAttribute($attribute); 164 | $this->updateSiblingsPosition(0, [$attribute => $oldPosition]); 165 | if ($newPosition < $oldPosition) { 166 | $this->increaseSiblingsPosition( 167 | ['between', $attribute, $newPosition, $oldPosition] 168 | ); 169 | } else { 170 | $this->decreaseSiblingsPosition( 171 | ['between', $attribute, $oldPosition, $newPosition] 172 | ); 173 | } 174 | } 175 | } 176 | 177 | /** 178 | * Decreases the position by 1 of siblings with equal or bigger position as 179 | * the deleted record. 180 | */ 181 | public function afterDelete() 182 | { 183 | $this->decreaseSiblingsPosition([ 184 | '>', 185 | $this->positionAttribute, 186 | $this->owner->getAttribute($this->positionAttribute), 187 | ]); 188 | } 189 | 190 | /** 191 | * Update the position of siblings on the database. 192 | * @param integer|DbExpression $position the new position. 193 | * @param array $condition the extra condition to update siblings. 194 | * @return int the number of updated siblings. 195 | */ 196 | protected function updateSiblingsPosition( 197 | $position, 198 | array $condition, 199 | array $orderBy = [] 200 | ): int { 201 | $params = []; 202 | $queryBuilder = $this->owner->getDb()->getQueryBuilder(); 203 | return $this->owner->getDb()->createCommand( 204 | $queryBuilder->update( 205 | $this->owner->tableName(), 206 | [$this->positionAttribute => $position], 207 | [ 208 | 'and', 209 | [ 210 | $this->parentAttribute => $this->owner->getAttribute( 211 | $this->parentAttribute 212 | ) 213 | ], 214 | $condition, 215 | ], 216 | $params // params 217 | ) 218 | . ' ' . $queryBuilder->buildOrderBy($orderBy), 219 | $params 220 | )->execute(); 221 | } 222 | 223 | /** 224 | * Increases the position of siblings by 1. 225 | * 226 | * @param array $conditoin the extra condition to update siblings. 227 | * @return int the number of updated siblings. 228 | */ 229 | protected function increaseSiblingsPosition(array $condition): int 230 | { 231 | return $this->updateSiblingsPosition( 232 | $this->positionIncrease, 233 | $condition, 234 | [$this->positionAttribute => SORT_DESC] 235 | ); 236 | } 237 | 238 | /** 239 | * Decreases the position of siblings by 1. 240 | * 241 | * @param array $conditoin the extra condition to update siblings. 242 | * @return int the number of updated siblings. 243 | */ 244 | protected function decreaseSiblingsPosition(array $condition): int 245 | { 246 | return $this->updateSiblingsPosition( 247 | $this->positionDecrease, 248 | $condition, 249 | [$this->positionAttribute => SORT_ASC] 250 | ); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/fixtures/DataTypeFixture.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class DataTypeFixture extends \yii\test\ActiveFixture 12 | { 13 | /** 14 | * @inheritdoc 15 | */ 16 | public $modelClass = DataType::class; 17 | 18 | /** 19 | * @inheritdoc 20 | */ 21 | public $dataFile = __DIR__ . '/data/data_type.php'; 22 | } 23 | -------------------------------------------------------------------------------- /src/fixtures/data/data_type.php: -------------------------------------------------------------------------------- 1 | 'string', 9 | 'Label' => 'String', 10 | 'cast' => 'stringCast', 11 | 'created_by' => 1, 12 | 'created_at' => $now, 13 | 'updated_by' => 1, 14 | 'updated_at' => $now, 15 | ], 16 | [ 17 | 'name' => 'integer', 18 | 'label' => 'Integer', 19 | 'cast' => 'integerCast', 20 | 'created_by' => 1, 21 | 'created_at' => $now, 22 | 'updated_by' => 1, 23 | 'updated_at' => $now, 24 | ], 25 | [ 26 | 'name' => 'float', 27 | 'label' => 'Decimal', 28 | 'cast' => 'floatCast', 29 | 'created_by' => 1, 30 | 'created_at' => $now, 31 | 'updated_by' => 1, 32 | 'updated_at' => $now, 33 | ], 34 | [ 35 | 'name' => 'boolean', 36 | 'label' => 'Boolean', 37 | 'cast' => 'booleanCast', 38 | 'created_by' => 1, 39 | 'created_at' => $now, 40 | 'updated_by' => 1, 41 | 'updated_at' => $now, 42 | ], 43 | [ 44 | 'name' => 'file', 45 | 'label' => 'File', 46 | 'cast' => 'fileCast', 47 | 'created_by' => 1, 48 | 'created_at' => $now, 49 | 'updated_by' => 1, 50 | 'updated_at' => $now, 51 | ], 52 | ]; 53 | 54 | -------------------------------------------------------------------------------- /src/migrations/tables/m170101_000001_form.php: -------------------------------------------------------------------------------- 1 | $this->primaryKey(), 20 | 'name' => $this->string(64)->unique()->notNull(), 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/migrations/tables/m170101_000002_data_type.php: -------------------------------------------------------------------------------- 1 | $this->primaryKey(), 20 | 'name' => $this->string(32)->notNull()->unique(), 21 | 'label' => $this->text()->notNull(), 22 | 'cast' => $this->string(64)->notNull(), 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/migrations/tables/m170101_000003_field.php: -------------------------------------------------------------------------------- 1 | $this->primaryKey(), 20 | 'data_type_id' => $this->normalKey(), 21 | 'name' => $this->string(32)->unique()->notNull(), 22 | 'label' => $this->text()->notNull(), 23 | 'service' => $this->text()->defaultValue(null) 24 | ->comment('url for a web service to provide searcheable data'), 25 | ]; 26 | } 27 | 28 | /** 29 | * @inheritdoc 30 | */ 31 | public function foreignKeys() 32 | { 33 | return ['data_type_id' => 'formgenerator_data_type']; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/migrations/tables/m170101_000004_field_rule.php: -------------------------------------------------------------------------------- 1 | $this->primaryKey(), 20 | 'field_id' => $this->normalKey(), 21 | 'rule_class' => $this->string(64)->notNull(), 22 | ]; 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function foreignKeys() 29 | { 30 | return ['field_id' => 'formgenerator_field']; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/migrations/tables/m170101_000005_field_rule_property.php: -------------------------------------------------------------------------------- 1 | $this->normalKey(), 20 | 'property' => $this->string(64)->notNull(), 21 | 'value' => $this->string(64)->notNull(), 22 | ]; 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function foreignKeys() 29 | { 30 | return ['rule_id' => 'formgenerator_field_rule']; 31 | } 32 | 33 | /** 34 | * @inheritdoc 35 | */ 36 | public function compositePrimaryKeys() 37 | { 38 | return ['rule_id', 'property']; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/migrations/tables/m170101_000006_form_section.php: -------------------------------------------------------------------------------- 1 | $this->primaryKey(), 20 | 'position' => $this->integer()->unsigned()->notNull(), 21 | 'form_id' => $this->normalKey(), 22 | 'name' => $this->string(32)->notNull(), 23 | 'label' => $this->text()->notNull(), 24 | ]; 25 | } 26 | 27 | /** 28 | * @inheritdoc 29 | */ 30 | public function foreignKeys() 31 | { 32 | return ['form_id' => 'formgenerator_form']; 33 | } 34 | 35 | /** 36 | * @inheritdoc 37 | */ 38 | public function compositeUniqueKeys() 39 | { 40 | return [ 41 | 'name' => ['form_id', 'name'], 42 | 'position' => ['form_id', 'position'], 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/migrations/tables/m170101_000007_form_section_field.php: -------------------------------------------------------------------------------- 1 | $this->normalKey(), 20 | 'field_id' => $this->normalKey(), 21 | 'position' => $this->integer()->unsigned()->notNull(), 22 | 'label' => $this->text()->defaultValue(null) 23 | ->comment('When not set it will use the label on field.'), 24 | ]; 25 | } 26 | 27 | /** 28 | * @inheritdoc 29 | */ 30 | public function foreignKeys() 31 | { 32 | return [ 33 | 'section_id' => 'formgenerator_form_section', 34 | 'field_id' => 'formgenerator_field', 35 | ]; 36 | } 37 | 38 | /** 39 | * @inheritdoc 40 | */ 41 | public function compositePrimaryKeys() 42 | { 43 | return ['section_id', 'field_id']; 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function compositeUniqueKeys() 50 | { 51 | return ['position' => ['section_id', 'position']]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/migrations/tables/m170101_000008_form_solicitude.php: -------------------------------------------------------------------------------- 1 | $this->primaryKey(), 20 | 'form_id' => $this->normalKey(), 21 | ]; 22 | } 23 | 24 | /** 25 | * @inheritdoc 26 | */ 27 | public function foreignKeys() 28 | { 29 | return ['form_id' => 'formgenerator_form']; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/migrations/tables/m170101_000009_form_solicitude_value.php: -------------------------------------------------------------------------------- 1 | $this->normalKey(), 20 | 'section_id' => $this->normalKey(), 21 | 'field_id' => $this->normalKey(), 22 | 'value' => $this->string(128), 23 | ]; 24 | } 25 | 26 | /** 27 | * @inheritdoc 28 | */ 29 | public function foreignKeys() 30 | { 31 | return [ 32 | 'solicitude_id' => 'formgenerator_solicitude', 33 | 'field' => [ 34 | 'table' => 'formgenerator_form_section_field', 35 | 'columns' => [ 36 | 'section_id' => 'section_id', 37 | 'field_id' => 'field_id', 38 | ], 39 | ], 40 | ]; 41 | } 42 | 43 | /** 44 | * @inheritdoc 45 | */ 46 | public function compositePrimaryKeys() 47 | { 48 | return [ 49 | 'solicitude_id', 50 | 'section_id', 51 | 'field_id', 52 | ]; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/models/DataType.php: -------------------------------------------------------------------------------- 1 | 'integer']; 40 | } 41 | 42 | /** 43 | * @inheritdoc 44 | */ 45 | public function rules() 46 | { 47 | return [ 48 | [['name', 'label', 'cast'], 'required'], 49 | [['name', 'label', 'cast',], 'string', 'min' => 4], 50 | [['name'], 'unique'], 51 | [['cast'], 'verifyCast'], 52 | ]; 53 | } 54 | 55 | /** 56 | * @return Callable 57 | */ 58 | protected function getCastCallable() 59 | { 60 | $values = explode(':', $this->cast, 2); 61 | return isset($values[1]) 62 | ? [$values[0], $values[1]] 63 | : [static::class, $values[0]]; 64 | } 65 | 66 | /** 67 | * Cast the value of an attribute in a model. 68 | * 69 | * @param SolicitudeValue $model 70 | * @param string $attribute 71 | */ 72 | public function castValue(SolicitudeValue $model, $attribute) 73 | { 74 | $callable = $this->getCastCallable(); 75 | $model->$attribute = $callable($model->$attribute, $attribute); 76 | } 77 | 78 | /** 79 | * Verify that the cast saved is callable. 80 | * 81 | * @param string $attribute 82 | */ 83 | public function verifyCast($attribute) 84 | { 85 | if (!is_callable($this->getCastCallable())) { 86 | $this->addError( 87 | $attribute, 88 | '`cast` must be an statically callable method.' 89 | ); 90 | } 91 | } 92 | 93 | public static function booleanCast($value, $attribute) 94 | { 95 | return (bool)$value; 96 | } 97 | 98 | public static function integerCast($value, $attribute) 99 | { 100 | return (int)$value; 101 | } 102 | 103 | public static function stringCast($value, $attribute) 104 | { 105 | return (string)$value; 106 | } 107 | 108 | public static function floatCast($value, $attribute) 109 | { 110 | return (float)$value; 111 | } 112 | 113 | public static function fileCast($value, $attribute) 114 | { 115 | if (null !== ($uploadedFile = UploadedFile::getInstanceByName( 116 | $attribute 117 | ))) { 118 | return $uploadedFile; 119 | } 120 | return $value; 121 | } 122 | 123 | /** 124 | * @inheritdoc 125 | */ 126 | public function attributeLabels() 127 | { 128 | return array_merge([ 129 | 'id' => 'ID', 130 | 'name' => 'Data Type name', 131 | 'label' => 'Label', 132 | 'cast' => 'Type Cast Method', 133 | ], parent::attributeLabels()); 134 | } 135 | 136 | /** 137 | * @return ActiveQuery 138 | */ 139 | public function getFields(): ActiveQuery 140 | { 141 | return $this->hasMany($this->fieldClass, ['data_type_id' => 'id']) 142 | ->inverseOf('dataType'); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/models/Field.php: -------------------------------------------------------------------------------- 1 | 'integer', 48 | 'data_type_id' => 'integer', 49 | ]; 50 | } 51 | 52 | /** 53 | * @inheritdoc 54 | */ 55 | public function rules() 56 | { 57 | return [ 58 | [['data_type_id', 'name', 'label'], 'required'], 59 | [['data_type_id'], 'integer'], 60 | [ 61 | ['data_type_id'], 62 | 'exist', 63 | 'skipOnError' => true, 64 | 'targetClass' => DataType::class, 65 | 'targetAttribute' => ['data_type_id' => 'id'], 66 | ], 67 | [['name', 'label', 'service'], 'string', 'min' => 4], 68 | [['name'], 'unique'], 69 | ]; 70 | } 71 | 72 | /** 73 | * @inheritdoc 74 | */ 75 | public function attributeLabels() 76 | { 77 | return array_merge([ 78 | 'id' => 'ID', 79 | 'data_type_id' => 'Data Type ID', 80 | 'name' => 'Field name', 81 | 'label' => 'Field label', 82 | ], parent::attributeLabels()); 83 | } 84 | 85 | /** 86 | * @return ActiveQuery 87 | */ 88 | public function getDataType(): ActiveQuery 89 | { 90 | return $this->hasOne($this->dataTypeClass, ['id' => 'data_type_id']); 91 | } 92 | 93 | /** 94 | * @return ActiveQuery 95 | */ 96 | public function getRules(): ActiveQuery 97 | { 98 | return $this->hasMany($this->ruleClass, ['field_id' => 'id']) 99 | ->inverseOf('field'); 100 | } 101 | 102 | /** 103 | * @return \yii\validators\Validator[] 104 | */ 105 | public function buildValidators(Model $model, $attributes) 106 | { 107 | $validators = []; 108 | 109 | foreach ($this->rules as $rule) { 110 | $validators[] = $rule->buildValidator($model, $attributes); 111 | } 112 | 113 | return $validators; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/models/FieldRule.php: -------------------------------------------------------------------------------- 1 | 'integer', 49 | 'field_id' => 'integer', 50 | ]; 51 | } 52 | 53 | /** 54 | * @inheritdoc 55 | */ 56 | public function rules() 57 | { 58 | return [ 59 | [['field_id', 'rule_class'], 'required'], 60 | [['field_id'], 'integer'], 61 | [ 62 | ['field_id'], 63 | 'exist', 64 | 'skipOnError' => true, 65 | 'targetClass' => Field::class, 66 | 'targetAttribute' => ['field_id' => 'id'], 67 | ], 68 | [['rule_class'], 'string', 'min' => 2], 69 | // todo check its a valid validator class 70 | ]; 71 | } 72 | 73 | /** 74 | * @inheritdoc 75 | */ 76 | public function attributeLabels() 77 | { 78 | return array_merge([ 79 | 'field_id' => 'Field ID', 80 | 'rule_class' => 'Rule Class', 81 | ], parent::attributeLabels()); 82 | } 83 | 84 | /** 85 | * @return ActiveQuery 86 | */ 87 | public function getField(): ActiveQuery 88 | { 89 | return $this->hasOne($this->fieldClass, ['id' => 'field_id']); 90 | } 91 | 92 | /** 93 | * @return ActiveQuery 94 | */ 95 | public function getProperties(): ActiveQuery 96 | { 97 | return $this->hasMany($this->propertyClass, ['rule_id' => 'id']) 98 | ->inverseOf('rule'); 99 | } 100 | 101 | /** 102 | * @param Model $model 103 | * @param array $attributes 104 | */ 105 | public function buildValidator(Model $model, $attributes) 106 | { 107 | return Validator::createValidator( 108 | $this->rule_class, 109 | $model, 110 | (array) $attributes, 111 | ArrayHelper::map($this->properties, 'property', 'value') 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/models/FieldRuleProperty.php: -------------------------------------------------------------------------------- 1 | 'integer']; 38 | } 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | public function rules() 44 | { 45 | return [ 46 | [['rule_id', 'property', 'value'], 'required'], 47 | [['rule_id'], 'integer'], 48 | [ 49 | ['rule_id'], 50 | 'exist', 51 | 'skipOnError' => true, 52 | 'targetClass' => FieldRule::class, 53 | 'targetAttribute' => ['rule_id' => 'id'], 54 | ], 55 | [['property', 'value'], 'trim'], 56 | [['property', 'value'], 'string'], 57 | [ 58 | ['property'], 59 | 'unique', 60 | 'targetAttribute' => ['rule_id', 'property'], 61 | 'message' => 'Property already in use for this rule.', 62 | ], 63 | ]; 64 | } 65 | 66 | /** 67 | * @inheritdoc 68 | */ 69 | public function attributeLabels() 70 | { 71 | return array_merge([ 72 | 'rule_id' => 'Rule ID', 73 | 'property' => 'Property', 74 | 'value' => 'Value', 75 | ], parent::attributeLabels()); 76 | } 77 | 78 | /** 79 | * @return ActiveQuery 80 | */ 81 | public function getRule(): ActiveQuery 82 | { 83 | return $this->hasOne($this->ruleClass, ['id' => 'rule_id']); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/models/Form.php: -------------------------------------------------------------------------------- 1 | 'integer']; 37 | } 38 | 39 | /** 40 | * @inheritdoc 41 | */ 42 | public function rules() 43 | { 44 | return [ 45 | [['name'], 'required'], 46 | [['name'], 'string', 'min' => 6], 47 | [['name'], 'unique'], 48 | ]; 49 | } 50 | 51 | /** 52 | * @inheritdoc 53 | */ 54 | public function attributeLabels() 55 | { 56 | return array_merge([ 57 | 'id' => 'ID', 58 | 'name' => 'Form name', 59 | ], parent::attributeLabels()); 60 | } 61 | 62 | /** 63 | * @return ActiveQuery 64 | */ 65 | public function getSections(): ActiveQuery 66 | { 67 | return $this->hasMany($this->sectionClass, ['form_id' => 'id']) 68 | ->inverseOf('form'); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/models/Section.php: -------------------------------------------------------------------------------- 1 | 'integer', 56 | 'form_id' => 'integer', 57 | ]; 58 | } 59 | 60 | /** 61 | * @inheritdoc 62 | */ 63 | public function transactions() 64 | { 65 | return [ 66 | self::SCENARIO_DEFAULT => self::OP_ALL, 67 | ]; 68 | } 69 | 70 | /** 71 | * @inheritdoc 72 | */ 73 | public function behaviors() 74 | { 75 | return parent::behaviors() + [ 76 | 'position' => [ 77 | 'class' => Positionable::class, 78 | 'parentAttribute' => 'form_id', 79 | ] 80 | ]; 81 | } 82 | 83 | /** 84 | * @inheritdoc 85 | */ 86 | public function rules() 87 | { 88 | return [ 89 | [['form_id', 'name', 'label'], 'required'], 90 | [['form_id', 'position'], 'integer'], 91 | [ 92 | ['form_id'], 93 | 'exist', 94 | 'skipOnError' => true, 95 | 'targetClass' => Form::class, 96 | 'targetAttribute' => ['form_id' => 'id'], 97 | ], 98 | [['name', 'label'], 'trim'], 99 | [['name', 'label'], 'string', 'min' => 6], 100 | [ 101 | ['name'], 102 | 'unique', 103 | 'targetAttribute' => ['form_id', 'name'], 104 | 'message' => 'Section name "{value}" already in use ' 105 | . 'for this form.', 106 | ], 107 | ]; 108 | } 109 | 110 | /** 111 | * @inheritdoc 112 | */ 113 | public function attributeLabels() 114 | { 115 | return array_merge([ 116 | 'id' => 'ID', 117 | 'form_id' => 'Form ID', 118 | 'name' => 'Section name', 119 | 'label' => 'Section label', 120 | ], parent::attributeLabels()); 121 | } 122 | 123 | /** 124 | * @return ActiveQuery 125 | */ 126 | public function getForm(): ActiveQuery 127 | { 128 | return $this->hasOne($this->formClass, ['id' => 'form_id']); 129 | } 130 | 131 | /** 132 | * @return ActiveQuery 133 | */ 134 | public function getSectionFields(): ActiveQuery 135 | { 136 | return $this->hasMany($this->sectionFieldClass, ['section_id' => 'id']) 137 | ->inverseOf('section'); 138 | } 139 | 140 | /** 141 | * @return ActiveQuery sibling stages for the same workflow 142 | */ 143 | public function getFields(): ActiveQuery 144 | { 145 | return $this->hasMany($this->fieldClass, ['id' => 'field_id']) 146 | ->via('sectionFields'); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/models/SectionField.php: -------------------------------------------------------------------------------- 1 | 'integer', 58 | 'field_id' => 'integer', 59 | ]; 60 | } 61 | 62 | /** 63 | * @inheritdoc 64 | */ 65 | public function rules() 66 | { 67 | return [ 68 | [['section_id', 'field_id'], 'required'], 69 | [['section_id', 'field_id', 'position'], 'integer'], 70 | [ 71 | ['section_id'], 72 | 'exist', 73 | 'skipOnError' => true, 74 | 'targetClass' => Section::class, 75 | 'targetAttribute' => ['section_id' => 'id'], 76 | ], 77 | [ 78 | ['field_id'], 79 | 'exist', 80 | 'skipOnError' => true, 81 | 'targetClass' => Field::class, 82 | 'targetAttribute' => ['field_id' => 'id'], 83 | 'message' => 'The field does not exists.', 84 | ], 85 | [['label'], 'trim'], 86 | [['label'], 'string'], 87 | [ 88 | ['field_id'], 89 | 'unique', 90 | 'targetAttribute' => ['section_id', 'field_id'], 91 | 'message' => 'Field already associated to the section.', 92 | ], 93 | ]; 94 | } 95 | 96 | /** 97 | * @inheritdoc 98 | */ 99 | public function transactions() 100 | { 101 | return [ 102 | self::SCENARIO_DEFAULT => self::OP_ALL, 103 | ]; 104 | } 105 | 106 | /** 107 | * @inheritdoc 108 | */ 109 | public function behaviors() 110 | { 111 | return parent::behaviors() + [ 112 | 'position' => [ 113 | 'class' => Positionable::class, 114 | 'parentAttribute' => 'section_id', 115 | ] 116 | ]; 117 | } 118 | 119 | /** 120 | * @inheritdoc 121 | */ 122 | public function attributeLabels() 123 | { 124 | return array_merge([ 125 | 'section_id' => 'Section ID', 126 | 'field_id' => 'Field ID', 127 | 'label' => 'label', 128 | ], parent::attributeLabels()); 129 | } 130 | 131 | /** 132 | * @return ActiveQuery 133 | */ 134 | public function getSection(): ActiveQuery 135 | { 136 | return $this->hasOne($this->sectionClass, ['id' => 'section_id']); 137 | } 138 | 139 | /** 140 | * @return ActiveQuery 141 | */ 142 | public function getField(): ActiveQuery 143 | { 144 | return $this->hasOne($this->fieldClass, ['id' => 'field_id']); 145 | } 146 | 147 | /** 148 | * @return ActiveQuery 149 | */ 150 | public function getSolicitudeValues(): ActiveQuery 151 | { 152 | return $this->hasMany($this->solicitudeValueClass, [ 153 | 'field_id' => 'field_id', 154 | 'section_id' => 'section_id', 155 | ])->inverseOf('sectionField'); 156 | } 157 | 158 | /** 159 | * @return ActiveQuery 160 | */ 161 | public function getSolicitudeValuesDetail(): ActiveQuery 162 | { 163 | return Yii::configure( 164 | $this->getSolicitudeValues(), 165 | ['multiple' => false] 166 | ) 167 | ->select([ 168 | 'count' => 'count(value)', 169 | 'countDistinct' => 'count(distinct value)', 170 | ]) 171 | ->groupBy(['section_id', 'field_id']) 172 | ->inverseOf(null) 173 | ->asArray(); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/models/Solicitude.php: -------------------------------------------------------------------------------- 1 | 'integer', 46 | 'form_id' => 'integer', 47 | ]; 48 | } 49 | 50 | /** 51 | * @inheritdoc 52 | */ 53 | public function rules() 54 | { 55 | return [ 56 | [['form_id'], 'required'], 57 | [['form_id'], 'integer'], 58 | [ 59 | ['form_id'], 60 | 'exist', 61 | 'skipOnError' => true, 62 | 'targetClass' => Form::class, 63 | 'targetAttribute' => ['form_id' => 'id'], 64 | ], 65 | ]; 66 | } 67 | 68 | /** 69 | * @inheritdoc 70 | */ 71 | public function attributeLabels() 72 | { 73 | return array_merge([ 74 | 'id' => 'ID', 75 | 'form_id' => 'Form ID', 76 | ], parent::attributeLabels()); 77 | } 78 | 79 | /** 80 | * @return ActiveQuery 81 | */ 82 | public function getForm(): ActiveQuery 83 | { 84 | return $this->hasOne($this->formClass, ['id' => 'form_id']); 85 | } 86 | 87 | /** 88 | * @return ActiveQuery 89 | */ 90 | public function getValues(): ActiveQuery 91 | { 92 | return $this->hasMany( 93 | $this->solicitudeValueClass, 94 | ['solicitude_id' => 'id'] 95 | )->inverseOf('solicitude'); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/models/SolicitudeValue.php: -------------------------------------------------------------------------------- 1 | 'integer', 61 | 'field_id' => 'integer', 62 | 'solicitude_id' => 'integer', 63 | ]; 64 | } 65 | 66 | /** 67 | * @inheritdoc 68 | */ 69 | public function rules() 70 | { 71 | return [ 72 | [['section_id', 'field_id', 'solicitude_id'], 'required'], 73 | [['section_id', 'field_id', 'solicitude_id'], 'integer'], 74 | [ 75 | ['solicitude_id'], 76 | 'exist', 77 | 'skipOnError' => true, 78 | 'targetClass' => Solicitude::class, 79 | 'targetAttribute' => ['solicitude_id' => 'id'], 80 | ], 81 | [ 82 | ['section_id'], 83 | 'exist', 84 | 'skipOnError' => true, 85 | 'targetClass' => Section::class, 86 | 'targetAttribute' => ['section_id' => 'id'], 87 | 'when' => function () { 88 | return !$this->hasErrors('solicitude_id'); 89 | }, 90 | 'filter' => function ($query) { 91 | $query->andWhere(['form_id' => $this->solicitude->form_id]); 92 | }, 93 | 'message' => 'Section is not associated to the form.', 94 | ], 95 | [ 96 | ['field_id'], 97 | 'exist', 98 | 'skipOnError' => true, 99 | 'targetClass' => SectionField::class, 100 | 'targetAttribute' => [ 101 | 'section_id' => 'section_id', 102 | 'field_id' => 'field_id', 103 | ], 104 | 'when' => function () { 105 | return !$this->hasErrors('section_id'); 106 | }, 107 | 'message' => 'Field not associated to the Section.', 108 | ], 109 | [ 110 | ['field_id'], 111 | 'unique', 112 | 'targetAttribute' => [ 113 | 'section_id', 114 | 'field_id', 115 | 'solicitude_id', 116 | ], 117 | 'when' => function () { 118 | return !$this->hasErrors('section_id') 119 | && !$this->hasErrors('solicitude_id'); 120 | }, 121 | 'message' => 'Field already filled.', 122 | ], 123 | [['value'], 'trim'], 124 | ]; 125 | } 126 | 127 | /** 128 | * @inheritdoc 129 | */ 130 | public function afterValidate() 131 | { 132 | if (!$this->hasErrors()) { 133 | $field = $this->getField() 134 | ->with([ 135 | 'dataType', 136 | 'rules' => function ($query) { 137 | $query->modelClass = 'tecnocen\\formgenerator\\models\\FieldRule'; 138 | }, 139 | 'rules.properties', 140 | ]) 141 | ->one(); 142 | $this->populateRelation('field', $field); 143 | foreach ($field->buildValidators($this, 'value') 144 | as $validator 145 | ) { 146 | $validator->validateAttributes($this, ['value']); 147 | } 148 | $field->dataType->castValue($this, 'value'); 149 | } 150 | parent::afterValidate(); 151 | } 152 | 153 | /** 154 | * @inheritdoc 155 | */ 156 | public function attributeLabels() 157 | { 158 | return array_merge([ 159 | 'section_id' => 'Section ID', 160 | 'field_id' => 'Field ID', 161 | 'label' => 'label', 162 | ], parent::attributeLabels()); 163 | } 164 | 165 | /** 166 | * @return ActiveQuery 167 | */ 168 | public function getSectionField(): ActiveQuery 169 | { 170 | return $this->hasOne( 171 | $this->sectionFieldClass, 172 | ['section_id' => 'section_id', 'field_id' => 'field_id'] 173 | ); 174 | } 175 | 176 | /** 177 | * @return ActiveQuery 178 | */ 179 | public function getSection(): ActiveQuery 180 | { 181 | return $this->hasOne($this->sectionClass, ['id' => 'section_id']); 182 | } 183 | 184 | /** 185 | * @return ActiveQuery 186 | */ 187 | public function getField(): ActiveQuery 188 | { 189 | return $this->hasOne($this->fieldClass, ['id' => 'field_id']); 190 | } 191 | 192 | /** 193 | * @return ActiveQuery 194 | */ 195 | public function getSolicitude(): ActiveQuery 196 | { 197 | return $this->hasOne($this->solicitudeClass, ['id' => 'solicitude_id']); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/roa/models/DataType.php: -------------------------------------------------------------------------------- 1 | getSelfLink(); 32 | 33 | return array_merge($this->getContractLinks(), [ 34 | 'curies' => [ 35 | new Link([ 36 | 'name' => 'embeddable', 37 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 38 | 'title' => 'Embeddable and not Nestable related resources.', 39 | ]), 40 | ], 41 | 'embeddable:fields' => 'fields', 42 | ]); 43 | } 44 | 45 | /** 46 | * @inheritdoc 47 | */ 48 | protected function slugBehaviorConfig(): array 49 | { 50 | return ['resourceName' => 'data-type']; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/roa/models/DataTypeSearch.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class DataTypeSearch extends DataType implements ResourceSearch 13 | { 14 | /** 15 | * @inhertidoc 16 | */ 17 | protected function slugBehaviorConfig(): array 18 | { 19 | return [ 20 | 'idAttribute' => [], 21 | 'resourceName' => 'data-type', 22 | ]; 23 | } 24 | 25 | /** 26 | * @inhertidoc 27 | */ 28 | public function rules() 29 | { 30 | return [ 31 | [['name', 'label'], 'string'], 32 | [['created_by'], 'integer'], 33 | ]; 34 | } 35 | 36 | /** 37 | * @inhertidoc 38 | */ 39 | public function search( 40 | array $params, 41 | ?string $formName = '' 42 | ): ?ActiveDataProvider { 43 | $this->load($params, $formName); 44 | if (!$this->validate()) { 45 | return null; 46 | } 47 | $class = get_parent_class(); 48 | 49 | return new ActiveDataProvider([ 50 | 'query' => $class::find()->andFilterWhere([ 51 | 'created_by' => $this->created_by, 52 | ]) 53 | ->andFilterWhere(['like', 'name', $this->name]) 54 | ->andFilterWhere(['like', 'label', $this->label]), 55 | ]); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/roa/models/Field.php: -------------------------------------------------------------------------------- 1 | 'field', 38 | 'checkAccess' => function ($params) { 39 | if (isset($params['field_id']) 40 | && $params['field_id'] != $this->id 41 | ) { 42 | throw new NotFoundHttpException( 43 | 'Field doesnt contain the requested route.' 44 | ); 45 | } 46 | }, 47 | ]; 48 | } 49 | 50 | /** 51 | * @inheritdoc 52 | */ 53 | public function getLinks() 54 | { 55 | $selfLink = $this->getSelfLink(); 56 | 57 | return array_merge($this->getContractLinks(), [ 58 | 'rules' => $selfLink . '/rule', 59 | 'curies' => [ 60 | new Link([ 61 | 'name' => 'embeddable', 62 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 63 | 'title' => 'Embeddable and not Nestable related resources.', 64 | ]), 65 | new Link([ 66 | 'name' => 'nestable', 67 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 68 | 'title' => 'Embeddable and Nestable related resources.', 69 | ]), 70 | ], 71 | 'embeddable:rules' => 'rules', 72 | 'nestable:dataType' => 'dataType', 73 | ]); 74 | } 75 | 76 | /** 77 | * @inheritdoc 78 | */ 79 | public function extraFields() 80 | { 81 | return ['dataType', 'rules']; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/roa/models/FieldRule.php: -------------------------------------------------------------------------------- 1 | 'rule', 38 | 'parentSlugRelation' => 'field', 39 | 'checkAccess' => function ($params) { 40 | if (isset($params['rule_id']) 41 | && $params['rule_id'] != $this->id 42 | ) { 43 | throw new NotFoundHttpException( 44 | 'Field Rule doesnt contain the requested route.' 45 | ); 46 | } 47 | } 48 | ]; 49 | } 50 | 51 | /** 52 | * @inheritdoc 53 | */ 54 | public function getLinks() 55 | { 56 | $selfLink = $this->getSelfLink(); 57 | 58 | return array_merge($this->getContractLinks(), [ 59 | 'properties' => $selfLink . '/property', 60 | 'curies' => [ 61 | new Link([ 62 | 'name' => 'embeddable', 63 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 64 | 'title' => 'Embeddable and not Nestable related resources.', 65 | ]), 66 | new Link([ 67 | 'name' => 'nestable', 68 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 69 | 'title' => 'Embeddable and Nestable related resources.', 70 | ]), 71 | ], 72 | 'embeddable:properties' => 'properties', 73 | 'nestable:field' => 'field', 74 | ]); 75 | } 76 | 77 | /** 78 | * @inheritdoc 79 | */ 80 | public function extraFields() 81 | { 82 | return ['field', 'properties']; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/roa/models/FieldRuleProperty.php: -------------------------------------------------------------------------------- 1 | getSelfLink(); 32 | 33 | return array_merge($this->getContractLinks(), [ 34 | 'properties' => $selfLink . '/property', 35 | 'curies' => [ 36 | new Link([ 37 | 'name' => 'nestable', 38 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 39 | 'title' => 'Embeddable and Nestable related resources.', 40 | ]), 41 | ], 42 | 'nestable:rule' => 'rule', 43 | ]); 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | protected function slugBehaviorConfig(): array 50 | { 51 | return [ 52 | 'idAttribute' => 'property', 53 | 'resourceName' => 'property', 54 | 'parentSlugRelation' => 'rule', 55 | ]; 56 | } 57 | 58 | /** 59 | * @inheritdoc 60 | */ 61 | public function extraFields() 62 | { 63 | return ['rule']; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/roa/models/FieldRulePropertySearch.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class FieldRulePropertySearch extends FieldRuleProperty implements 14 | ResourceSearch 15 | { 16 | /** 17 | * @inhertidoc 18 | */ 19 | protected function slugBehaviorConfig(): array 20 | { 21 | return [ 22 | 'idAttribute' => [], 23 | 'resourceName' => 'property', 24 | ]; 25 | } 26 | 27 | /** 28 | * @inhertidoc 29 | */ 30 | public function rules() 31 | { 32 | return [ 33 | [['rule_id'], 'required'], 34 | [['rule_id', 'created_by'], 'integer'], 35 | ]; 36 | } 37 | 38 | /** 39 | * @inhertidoc 40 | */ 41 | public function search( 42 | array $params, 43 | ?string $formName = '' 44 | ): ?ActiveDataProvider { 45 | $this->load($params, $formName); 46 | if (!$this->validate()) { 47 | return null; 48 | } 49 | if (null === $this->rule) { 50 | throw new NotFoundHttpException('Rule not found'); 51 | } 52 | $this->rule->checkAccess($params); 53 | $class = get_parent_class(); 54 | 55 | return new ActiveDataProvider([ 56 | 'query' => $class::find()->andFilterWhere([ 57 | 'rule_id' => $this->rule_id, 58 | 'created_by' => $this->created_by, 59 | ]), 60 | ]); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/roa/models/FieldSearch.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class FieldSearch extends Field implements ResourceSearch 13 | { 14 | /** 15 | * @inhertidoc 16 | */ 17 | protected function slugBehaviorConfig(): array 18 | { 19 | return [ 20 | 'idAttribute' => [], 21 | 'resourceName' => 'field', 22 | ]; 23 | } 24 | 25 | /** 26 | * @inhertidoc 27 | */ 28 | public function rules() 29 | { 30 | return [ 31 | [['data_type_id', 'created_by'], 'integer'], 32 | [['name', 'label'], 'string'], 33 | ]; 34 | } 35 | 36 | /** 37 | * @inhertidoc 38 | */ 39 | public function search( 40 | array $params, 41 | ?string $formName = '' 42 | ): ?ActiveDataProvider { 43 | $this->load($params, $formName); 44 | if (!$this->validate()) { 45 | return null; 46 | } 47 | 48 | $class = get_parent_class(); 49 | 50 | return new ActiveDataProvider([ 51 | 'query' => $class::find()->andFilterWhere([ 52 | 'created_by' => $this->created_by, 53 | 'data_type_id' => $this->data_type_id, 54 | ]) 55 | ->andFilterWhere(['like', 'name', $this->name]) 56 | ->andFilterWhere(['like', 'label', $this->label]), 57 | ]); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/roa/models/Form.php: -------------------------------------------------------------------------------- 1 | getSelfLink(); 32 | 33 | return array_merge($this->getContractLinks(), [ 34 | 'sections' => $selfLink . '/section', 35 | 'curies' => [ 36 | new Link([ 37 | 'name' => 'embeddable', 38 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 39 | 'title' => 'Embeddable and not Nestable related resources.', 40 | ]), 41 | ], 42 | 'embeddable:sections' => 'sections', 43 | ]); 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | protected function slugBehaviorConfig(): array 50 | { 51 | return [ 52 | 'resourceName' => 'form', 53 | 'checkAccess' => function ($params) { 54 | if (isset($params['form_id']) 55 | && $params['form_id'] != $this->id 56 | ) { 57 | throw new NotFoundHttpException( 58 | 'Field doesnt contain the requested route.' 59 | ); 60 | } 61 | }, 62 | ]; 63 | } 64 | 65 | /** 66 | * @inheritdoc 67 | */ 68 | public function extraFields() 69 | { 70 | return ['sections']; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/roa/models/FormSearch.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class FormSearch extends Form implements ResourceSearch 14 | { 15 | /** 16 | * @inhertidoc 17 | */ 18 | protected function slugBehaviorConfig(): array 19 | { 20 | return [ 21 | 'idAttribute' => [], 22 | 'resourceName' => 'form', 23 | ]; 24 | } 25 | 26 | /** 27 | * @inhertidoc 28 | */ 29 | public function rules() 30 | { 31 | return [ 32 | [['created_by'], 'integer'], 33 | [['name'], 'string'], 34 | ]; 35 | } 36 | 37 | /** 38 | * @inhertidoc 39 | */ 40 | public function search( 41 | array $params, 42 | ?string $formName = '' 43 | ): ?ActiveDataProvider { 44 | $this->load($params, $formName); 45 | if (!$this->validate()) { 46 | return null; 47 | } 48 | $class = get_parent_class(); 49 | 50 | return new ActiveDataProvider([ 51 | 'query' => $class::find()->andFilterWhere([ 52 | 'created_by' => $this->created_by, 53 | ]) 54 | ->andFilterWhere(['like', 'name', $this->name]), 55 | ]); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/roa/models/Section.php: -------------------------------------------------------------------------------- 1 | getSelfLink(); 41 | 42 | return array_merge($this->getContractLinks(), [ 43 | 'fields' => $selfLink . '/field', 44 | 'curies' => [ 45 | new Link([ 46 | 'name' => 'embeddable', 47 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 48 | 'title' => 'Embeddable and not Nestable related resources.', 49 | ]), 50 | new Link([ 51 | 'name' => 'nestable', 52 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 53 | 'title' => 'Embeddable and Nestable related resources.', 54 | ]), 55 | ], 56 | 'embeddable:fields' => 'fields', 57 | 'nestable:form' => 'form', 58 | ]); 59 | } 60 | 61 | /** 62 | * @inheritdoc 63 | */ 64 | protected function slugBehaviorConfig(): array 65 | { 66 | return [ 67 | 'resourceName' => 'section', 68 | 'parentSlugRelation' => 'form', 69 | ]; 70 | } 71 | 72 | /** 73 | * @inheritdoc 74 | */ 75 | public function extraFields() 76 | { 77 | return ['form', 'fields', 'sectionFields']; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/roa/models/SectionField.php: -------------------------------------------------------------------------------- 1 | getSelfLink(); 41 | 42 | return array_merge($this->getContractLinks(), [ 43 | 'field' => $this->field->getSelfLink(), 44 | ]); 45 | } 46 | 47 | /** 48 | * @inheritdoc 49 | */ 50 | protected function slugBehaviorConfig(): array 51 | { 52 | return [ 53 | 'idAttribute' => 'field_id', 54 | 'resourceName' => 'field', 55 | 'parentSlugRelation' => 'section', 56 | ]; 57 | } 58 | 59 | /** 60 | * @inheritdoc 61 | */ 62 | public function extraFields() 63 | { 64 | return ['field', 'section', 'solicitudeValuesDetail']; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/roa/models/SectionFieldSearch.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class SectionFieldSearch extends SectionField implements ResourceSearch 14 | { 15 | /** 16 | * @inhertidoc 17 | */ 18 | protected function slugBehaviorConfig(): array 19 | { 20 | return [ 21 | 'idAttribute' => [], 22 | 'resourceName' => 'field', 23 | 'parentSlugRelation' => 'section', 24 | ]; 25 | } 26 | 27 | /** 28 | * @inhertidoc 29 | */ 30 | public function rules() 31 | { 32 | return [ 33 | [['section_id'], 'required'], 34 | [['section_id', 'position', 'created_by'], 'integer'], 35 | [['label'], 'string'], 36 | ]; 37 | } 38 | 39 | /** 40 | * @inhertidoc 41 | */ 42 | public function search( 43 | array $params, 44 | ?string $formName = '' 45 | ): ?ActiveDataProvider { 46 | $this->load($params, $formName); 47 | $this->getBehavior('position')->attachValidators = false; 48 | if (!$this->validate()) { 49 | return null; 50 | } 51 | 52 | $this->checkAccess($params); 53 | $class = get_parent_class(); 54 | 55 | return new ActiveDataProvider([ 56 | 'query' => $class::find()->andFilterWhere([ 57 | 'section_id' => $this->section_id, 58 | 'created_by' => $this->created_by, 59 | 'position' => $this->position, 60 | ]) 61 | ->andFilterWhere(['like', 'label', $this->label]), 62 | 'sort' => [ 63 | 'defaultOrder' => [ 64 | 'position' => SORT_ASC, 65 | ], 66 | ], 67 | ]); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/roa/models/SectionSearch.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class SectionSearch extends Section implements ResourceSearch 14 | { 15 | /** 16 | * @inhertidoc 17 | */ 18 | protected function slugBehaviorConfig(): array 19 | { 20 | return [ 21 | 'idAttribute' => [], 22 | 'parentSlugRelation' => 'form', 23 | 'resourceName' => 'section', 24 | ]; 25 | } 26 | 27 | /** 28 | * @inhertidoc 29 | */ 30 | public function rules() 31 | { 32 | return [ 33 | [['form_id'], 'required'], 34 | [['form_id', 'position', 'created_by'], 'integer'], 35 | [['name', 'label'], 'string'], 36 | ]; 37 | } 38 | 39 | /** 40 | * @inhertidoc 41 | */ 42 | public function search( 43 | array $params, 44 | ?string $formName = '' 45 | ): ?ActiveDataProvider { 46 | $this->load($params, $formName); 47 | $this->getBehavior('position')->attachValidators = false; 48 | if (!$this->validate()) { 49 | return null; 50 | } 51 | $class = get_parent_class(); 52 | 53 | return new ActiveDataProvider([ 54 | 'query' => $class::find()->andFilterWhere([ 55 | 'form_id' => $this->form_id, 56 | 'created_by' => $this->created_by, 57 | 'position' => $this->position, 58 | ]) 59 | ->andFilterWhere(['like', 'name', $this->name]) 60 | ->andFilterWhere(['like', 'label', $this->label]), 61 | 'sort' => [ 62 | 'defaultOrder' => [ 63 | 'position' => SORT_ASC, 64 | ] 65 | ] 66 | ]); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/roa/models/Solicitude.php: -------------------------------------------------------------------------------- 1 | getSelfLink(); 37 | 38 | return array_merge($this->getContractLinks(), [ 39 | 'values' => $selfLink . '/value', 40 | 'curies' => [ 41 | new Link([ 42 | 'name' => 'embeddable', 43 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 44 | 'title' => 'Embeddable and not Nestable related resources.', 45 | ]), 46 | new Link([ 47 | 'name' => 'nestable', 48 | 'href' => Url::to($selfLink, ['expand' => '{rel}']), 49 | 'title' => 'Embeddable and Nestable related resources.', 50 | ]), 51 | ], 52 | 'embeddable:values' => 'values', 53 | 'nestable:form' => 'form', 54 | ]); 55 | } 56 | 57 | /** 58 | * @inheritdoc 59 | */ 60 | protected function slugBehaviorConfig(): array 61 | { 62 | return [ 63 | 'resourceName' => 'solicitude', 64 | 'parentSlugRelation' => 'form', 65 | 'checkAccess' => function ($params) { 66 | if (isset($params['solicitude_id']) 67 | && $params['solicitude_id'] != $this->id 68 | ) { 69 | throw new NotFoundHttpException( 70 | 'Solicitude doesnt contain the requested route.' 71 | ); 72 | } 73 | }, 74 | ]; 75 | } 76 | 77 | /** 78 | * @inheritdoc 79 | */ 80 | public function extraFields() 81 | { 82 | return ['form', 'values']; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/roa/models/SolicitudeSearch.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class SolicitudeSearch extends Solicitude implements ResourceSearch 14 | { 15 | /** 16 | * @inhertidoc 17 | */ 18 | protected function slugBehaviorConfig(): array 19 | { 20 | return [ 21 | 'idAttribute' => [], 22 | 'parentSlugRelation' => 'form', 23 | 'resourceName' => 'form', 24 | ]; 25 | } 26 | 27 | /** 28 | * @inhertidoc 29 | */ 30 | public function attributes() 31 | { 32 | return array_merge(parent::attributes(), ['value']); 33 | } 34 | 35 | /** 36 | * @inhertidoc 37 | */ 38 | public function rules() 39 | { 40 | return [ 41 | [['form_id'], 'required'], 42 | [['form_id', 'created_by'], 'integer'], 43 | [['value'], 'default', 'value' => []], 44 | [['value'], 'validateValues'], 45 | ]; 46 | } 47 | 48 | public function validateValues($attribute, $params, $validator) 49 | { 50 | $values = $this->$attribute; 51 | if (!is_array($values)) { 52 | $validator->addError( 53 | $this, 54 | $attribute, 55 | '{attribute} must receive an array' 56 | ); 57 | return; 58 | } 59 | foreach ($values as $field_id => $value) { 60 | if (is_string($field_id)) { 61 | $validator->addError( 62 | $this, 63 | $attribute, 64 | '`value` only accepts key integers to filter by `field_id`.' 65 | ); 66 | } 67 | if (empty($value)) { 68 | $validator->addError( 69 | $this, 70 | $attribute, 71 | '`value` must not be empty.' 72 | ); 73 | } 74 | } 75 | } 76 | 77 | /** 78 | * @inhertidoc 79 | */ 80 | public function search( 81 | array $params, 82 | ?string $formName = '' 83 | ): ?ActiveDataProvider { 84 | $this->load($params, $formName); 85 | if (!$this->validate()) { 86 | return null; 87 | } 88 | $class = get_parent_class(); 89 | $query = $class::find()->andFilterWhere([ 90 | 'form_id' => $this->form_id, 91 | 'created_by' => $this->created_by, 92 | ]); 93 | 94 | foreach ($this->value as $field_id => $value) { 95 | $alias = "value_$field_id"; 96 | $query->andWhere([ 97 | 'exists', 98 | SolicitudeValue::find()->andWhere([ 99 | 'and', 100 | 'solicitude_id = id', 101 | ['field_id' => $field_id], 102 | [ 103 | 'like', 104 | 'value', 105 | $value, 106 | ], 107 | ]) 108 | ]); 109 | } 110 | 111 | return new ActiveDataProvider(['query' => $query]); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/roa/models/SolicitudeValue.php: -------------------------------------------------------------------------------- 1 | getSelfLink(); 46 | 47 | return array_merge($this->getContractLinks(), [ 48 | 'field' => $this->field->getSelfLink(), 49 | 'section' => $this->section->getSelfLink(), 50 | ]); 51 | } 52 | 53 | /** 54 | * @inheritdoc 55 | */ 56 | protected function slugBehaviorConfig(): array 57 | { 58 | return [ 59 | 'idAttribute' => ['section_id', 'field_id'], 60 | 'resourceName' => 'value', 61 | 'parentSlugRelation' => 'solicitude', 62 | ]; 63 | } 64 | 65 | /** 66 | * @inheritdoc 67 | */ 68 | public function extraFields() 69 | { 70 | return [ 71 | 'field', 72 | 'section', 73 | 'sectionField', 74 | 'solicitude', 75 | ]; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/roa/models/SolicitudeValueSearch.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class SolicitudeValueSearch extends SolicitudeValue implements ResourceSearch 15 | { 16 | /** 17 | * @inhertidoc 18 | */ 19 | protected function slugBehaviorConfig(): array 20 | { 21 | return [ 22 | 'idAttribute' => [], 23 | 'parentSlugRelation' => 'solicitude', 24 | 'resourceName' => 'value', 25 | ]; 26 | } 27 | 28 | /** 29 | * @inhertidoc 30 | */ 31 | public function rules() 32 | { 33 | return [ 34 | [['solicitude_id'], 'required'], 35 | [['solicitude_id', 'section_id', 'field_id'], 'integer'], 36 | [['value'], 'safe'], 37 | ]; 38 | } 39 | 40 | /** 41 | * @inhertidoc 42 | */ 43 | public function search( 44 | array $params, 45 | ?string $formName = '' 46 | ): ?ActiveDataProvider { 47 | $this->load($params, $formName); 48 | if (!$this->validate()) { 49 | return null; 50 | } 51 | $this->checkAccess($params); 52 | $class = get_parent_class(); 53 | 54 | return new ActiveDataProvider([ 55 | 'query' => $class::find()->innerJoinWith(['solicitude']) 56 | ->andFilterWhere([ 57 | 'solicitude_id' => $this->solicitude_id, 58 | 'section_id' => $this->section_id, 59 | 'field_id' => $this->field_id, 60 | ])->andFilterWhere(['like', 'value', $this->value]) 61 | ]); 62 | } 63 | 64 | /** 65 | * @inhertidoc 66 | */ 67 | public function afterValidate() 68 | { 69 | Model::afterValidate(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/roa/models/SolicitudeValueSimpleSearch.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class SolicitudeValueSimpleSearch extends SolicitudeValue implements 18 | \tecnocen\roa\ResourceSearch 19 | { 20 | /** 21 | * @inhertidoc 22 | */ 23 | protected function slugBehaviorConfig(): array 24 | { 25 | return [ 26 | 'idAttribute' => [], 27 | 'parentSlugRelation' => null, 28 | 'resourceName' => 'solicitude-value', 29 | ]; 30 | } 31 | 32 | /** 33 | * @inhertidoc 34 | */ 35 | public function attributes() 36 | { 37 | return array_merge(parent::attributes(), ['form_id']); 38 | } 39 | 40 | /** 41 | * @inhertidoc 42 | */ 43 | public function rules() 44 | { 45 | return [ 46 | [['form_id', 'solicitude_id', 'section_id', 'field_id'], 'integer'], 47 | [['value'], 'safe'], 48 | ]; 49 | } 50 | 51 | /** 52 | * @inhertidoc 53 | */ 54 | public function search( 55 | array $params, 56 | ?string $formName = '' 57 | ): ?ActiveDataProvider { 58 | $this->load($params, $formName); 59 | if (!$this->validate()) { 60 | return null; 61 | } 62 | $this->checkAccess($params); 63 | $class = get_parent_class(); 64 | 65 | return new ActiveDataProvider([ 66 | 'query' => $class::find()->innerJoinWith(['solicitude']) 67 | ->andFilterWhere([ 68 | 'form_id' => $this->form_id, 69 | 'solicitude_id' => $this->solicitude_id, 70 | 'section_id' => $this->section_id, 71 | 'field_id' => $this->field_id, 72 | ])->andFilterWhere(['like', 'value', $this->value]) 73 | ]); 74 | } 75 | 76 | /** 77 | * @inhertidoc 78 | */ 79 | public function afterValidate() 80 | { 81 | Model::afterValidate(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/roa/modules/Version.php: -------------------------------------------------------------------------------- 1 | /section'; 16 | const SECTION_FIELD_ROUTE = self::SECTION_ROUTE . '//field'; 17 | 18 | const DATA_TYPE_ROUTE = 'data-type'; 19 | 20 | const FIELD_ROUTE = 'field'; 21 | const FIELD_RULE_ROUTE = self::FIELD_ROUTE . '//rule'; 22 | const FIELD_RULE_PROPERTY_ROUTE = self::FIELD_RULE_ROUTE 23 | . '//property'; 24 | 25 | const SOLICITUDE_ROUTE = self::FORM_ROUTE . '//solicitude'; 26 | const SOLICITUDE_VALUE_ROUTE = self::SOLICITUDE_ROUTE 27 | . '//value'; 28 | 29 | const SOLICITUDE_VALUE_SEARCH_ROUTE = 'solicitude-value'; 30 | 31 | /** 32 | * @inheritdoc 33 | */ 34 | public $resources = [ 35 | self::FORM_ROUTE, 36 | self::SECTION_ROUTE, 37 | self::SECTION_FIELD_ROUTE, 38 | 39 | self::DATA_TYPE_ROUTE, 40 | 41 | self::FIELD_ROUTE, 42 | self::FIELD_RULE_ROUTE, 43 | self::FIELD_RULE_PROPERTY_ROUTE => [ 44 | 'class' => resources\field\rule\PropertyResource::class, 45 | 'urlRule' => ['tokens' => ['{id}' => '']], 46 | ], 47 | 48 | self::SOLICITUDE_ROUTE, 49 | self::SOLICITUDE_VALUE_ROUTE => [ 50 | 'class' => resources\form\solicitude\ValueResource::class, 51 | 'urlRule' => [ 52 | 'tokens' => [ 53 | '{section_id}' => '', 54 | '{id}' => '', 55 | ], 56 | 'patterns' => [ 57 | 'PUT,PATCH {section_id}/{id}' => 'update', 58 | 'DELETE {section_id}/{id}' => 'delete', 59 | 'GET,HEAD {section_id}/{id}' => 'view', 60 | 'POST' => 'create', 61 | 'GET,HEAD {section_id}' => 'index', 62 | 'GET,HEAD' => 'index', 63 | '{section_id}' => 'options', 64 | '{section_id}/{id}' => 'options', 65 | '' => 'options', 66 | ], 67 | ], 68 | ], 69 | self::SOLICITUDE_VALUE_SEARCH_ROUTE => [ 70 | 'class' => resources\SolicitudeValueResource::class, 71 | 'urlRule' => [ 72 | 'tokens' => [ 73 | ], 74 | 'patterns' => [ 75 | 'GET,HEAD' => 'index', 76 | '' => 'options', 77 | ], 78 | ], 79 | ], 80 | ]; 81 | } 82 | -------------------------------------------------------------------------------- /src/roa/resources/DataTypeResource.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class DataTypeResource extends \tecnocen\roa\controllers\Resource 13 | { 14 | /** 15 | * @inheritdoc 16 | */ 17 | public $modelClass = DataType::class; 18 | 19 | /** 20 | * @inheritdoc 21 | */ 22 | public $searchClass = DataTypeSearch::class; 23 | } 24 | -------------------------------------------------------------------------------- /src/roa/resources/FieldResource.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class FieldResource extends \tecnocen\roa\controllers\Resource 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $modelClass = Field::class; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $searchClass = FieldSearch::class; 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function baseQuery(): ActiveQuery 29 | { 30 | return parent::baseQuery()->alias('field')->with(['dataType']); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/roa/resources/FormResource.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class FormResource extends \tecnocen\roa\controllers\Resource 13 | { 14 | /** 15 | * @inheritdoc 16 | */ 17 | public $modelClass = Form::class; 18 | 19 | /** 20 | * @inheritdoc 21 | */ 22 | public $searchClass = FormSearch::class; 23 | } 24 | -------------------------------------------------------------------------------- /src/roa/resources/SolicitudeValueResource.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class SolicitudeValueResource extends \tecnocen\roa\controllers\Resource 13 | { 14 | /** 15 | * @inheritdoc 16 | */ 17 | public $modelClass = SolicitudeValue::class; 18 | 19 | /** 20 | * @inheritdoc 21 | */ 22 | public $searchClass = SolicitudeValueSimpleSearch::class; 23 | 24 | /** 25 | * @inheritdoc 26 | */ 27 | public function verbs() 28 | { 29 | return [ 30 | 'index' => ['GET', 'HEAD'], 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/roa/resources/field/RuleResource.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class RuleResource extends \tecnocen\roa\controllers\Resource 13 | { 14 | /** 15 | * @inheritdoc 16 | */ 17 | public $modelClass = FieldRule::class; 18 | 19 | /** 20 | * @inheritdoc 21 | */ 22 | public $filterParams = ['field_id', 'rule_class', 'created_by']; 23 | 24 | /** 25 | * @inheritdoc 26 | */ 27 | public function verbs() 28 | { 29 | $verbs = parent::verbs(); 30 | unset($verbs['update']); 31 | 32 | return $verbs; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/roa/resources/field/rule/PropertyResource.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class PropertyResource extends \tecnocen\roa\controllers\Resource 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $idAttribute = 'property'; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $modelClass = FieldRuleProperty::class; 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public $searchClass = FieldRulePropertySearch::class; 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public $filterParams = ['field_id', 'rule_id']; 34 | 35 | /** 36 | * @inheritdoc 37 | */ 38 | public function baseQuery(): ActiveQuery 39 | { 40 | return parent::baseQuery()->innerJoinWith(['rule']); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/roa/resources/form/SectionResource.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class SectionResource extends \tecnocen\roa\controllers\Resource 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $modelClass = Section::class; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $searchClass = SectionSearch::class; 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public $filterParams = ['form_id']; 29 | } 30 | -------------------------------------------------------------------------------- /src/roa/resources/form/SolicitudeResource.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class SolicitudeResource extends \tecnocen\roa\controllers\Resource 13 | { 14 | /** 15 | * @inheritdoc 16 | */ 17 | public $modelClass = Solicitude::class; 18 | 19 | /** 20 | * @inheritdoc 21 | */ 22 | public $searchClass = SolicitudeSearch::class; 23 | 24 | /** 25 | * @inheritdoc 26 | */ 27 | public $filterParams = ['form_id']; 28 | } 29 | -------------------------------------------------------------------------------- /src/roa/resources/form/section/FieldResource.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class FieldResource extends \tecnocen\roa\controllers\Resource 13 | { 14 | /** 15 | * @inheritdoc 16 | */ 17 | public $idAttribute = 'field_id'; 18 | 19 | /** 20 | * @inheritdoc 21 | */ 22 | public $modelClass = SectionField::class; 23 | 24 | /** 25 | * @inheritdoc 26 | */ 27 | public $searchClass = SectionFieldSearch::class; 28 | 29 | /** 30 | * @inheritdoc 31 | */ 32 | public $filterParams = ['section_id']; 33 | } 34 | -------------------------------------------------------------------------------- /src/roa/resources/form/solicitude/ValueResource.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class ValueResource extends \tecnocen\roa\controllers\Resource 13 | { 14 | /** 15 | * @inheritdoc 16 | */ 17 | public $idAttribute = 'field_id'; 18 | 19 | /** 20 | * @inheritdoc 21 | */ 22 | public $modelClass = SolicitudeValue::class; 23 | 24 | /** 25 | * @inheritdoc 26 | */ 27 | public $searchClass = SolicitudeValueSearch::class; 28 | 29 | /** 30 | * @inheritdoc 31 | */ 32 | public $filterParams = [ 33 | 'solicitude_id', 34 | 'section_id', 35 | ]; 36 | } 37 | -------------------------------------------------------------------------------- /tests/_app/assets/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /tests/_app/config/.gitignore: -------------------------------------------------------------------------------- 1 | db.local.php 2 | -------------------------------------------------------------------------------- /tests/_app/config/common.php: -------------------------------------------------------------------------------- 1 | dirname(__DIR__), 5 | 'language' => 'en-US', 6 | 'aliases' => [ 7 | '@tests' => dirname(dirname(__DIR__)), 8 | '@tecnocen/formgenerator' => dirname(dirname(dirname(__DIR__))) . '/src', 9 | '@tecnocen/oauth2server' => VENDOR_DIR . '/tecnocen/yii2-oauth2-server/src', 10 | ], 11 | 'components' => [ 12 | 'db' => require __DIR__ . '/db.php', 13 | ], 14 | ]; 15 | -------------------------------------------------------------------------------- /tests/_app/config/console.php: -------------------------------------------------------------------------------- 1 | 'yii2-test-console', 9 | 'components' => [ 10 | 'log' => null, 11 | 'cache' => null, 12 | ], 13 | 'controllerMap' => [ 14 | 'migrate' => [ 15 | 'class' => controllers\MigrateController::class, 16 | 'migrationPath' => null, 17 | 'migrationNamespaces' => [], 18 | ], 19 | 'serve' => [ 20 | 'class' => controllers\ServeController::class, 21 | 'docroot' => '@app', 22 | ], 23 | ], 24 | ] 25 | ); 26 | -------------------------------------------------------------------------------- /tests/_app/config/db.php: -------------------------------------------------------------------------------- 1 | 'yii\db\Connection', 5 | 'dsn' => 'mysql:host=localhost;dbname=yii2_formgenerator_test', 6 | 'username' => 'root', 7 | 'password' => '', 8 | 'charset' => 'utf8', 9 | ]; 10 | 11 | if (file_exists(__DIR__.'/db.local.php')) { 12 | $db = array_merge($db, require(__DIR__.'/db.local.php')); 13 | } 14 | 15 | return $db; 16 | -------------------------------------------------------------------------------- /tests/_app/config/test.php: -------------------------------------------------------------------------------- 1 | 'yii2-formgenerator-tests', 12 | 'bootstrap' => ['api'], 13 | 'modules' => [ 14 | 'api' => [ 15 | 'class' => tecnocen\roa\modules\ApiContainer::class, 16 | 'identityClass' => app\models\User::class, 17 | 'versions' => [ 18 | 'f1' => [ 19 | 'class' => FormgeneratorVersion::class, 20 | ], 21 | ], 22 | ], 23 | 'rmdb' => [ 24 | 'class' => tecnocen\rmdb\Module::class, 25 | ], 26 | ], 27 | 'components' => [ 28 | 'mailer' => [ 29 | 'useFileTransport' => true, 30 | ], 31 | 'user' => ['identityClass' => app\models\User::class], 32 | 'urlManager' => [ 33 | 'showScriptName' => true, 34 | 'enablePrettyUrl' => true, 35 | ], 36 | 'request' => [ 37 | 'cookieValidationKey' => 'test', 38 | 'enableCsrfValidation' => false, 39 | ], 40 | ], 41 | 'params' => [], 42 | ] 43 | ); 44 | -------------------------------------------------------------------------------- /tests/_app/config/web.php: -------------------------------------------------------------------------------- 1 | 'yii2-formgenerator-demo', 7 | 'bootstrap' => ['debug'], 8 | 'aliases' => [ 9 | '@vendor' => VENDOR_DIR, 10 | '@bower' => VENDOR_DIR . '/bower-asset', 11 | ], 12 | 'modules' => [ 13 | 'debug' => [ 14 | 'class' => yii\debug\Module::class, 15 | ], 16 | ], 17 | 'components' => [ 18 | 'assetManager' => [ 19 | 'basePath' => dirname(__DIR__) . '/assets', 20 | ], 21 | ] 22 | ] 23 | ); 24 | -------------------------------------------------------------------------------- /tests/_app/controllers/SiteController.php: -------------------------------------------------------------------------------- 1 | render('index'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/_app/fixtures/FieldFixture.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class FieldFixture extends \yii\test\ActiveFixture 13 | { 14 | /** 15 | * @inheritdoc 16 | */ 17 | public $modelClass = Field::class; 18 | 19 | /** 20 | * @inheritdoc 21 | */ 22 | public $dataFile = __DIR__ . '/data/field.php'; 23 | 24 | /** 25 | * @inheritdoc 26 | */ 27 | public $depends = [DataTypeFixture::class]; 28 | } 29 | -------------------------------------------------------------------------------- /tests/_app/fixtures/FieldRuleFixture.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class FieldRuleFixture extends \yii\test\ActiveFixture 12 | { 13 | /** 14 | * @inheritdoc 15 | */ 16 | public $modelClass = FieldRule::class; 17 | 18 | /** 19 | * @inheritdoc 20 | */ 21 | public $dataFile = __DIR__ . '/data/field_rule.php'; 22 | 23 | /** 24 | * @inheritdoc 25 | */ 26 | public $depends = [FieldFixture::class]; 27 | } 28 | -------------------------------------------------------------------------------- /tests/_app/fixtures/FieldRulePropertyFixture.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class FieldRulePropertyFixture extends \yii\test\ActiveFixture 12 | { 13 | /** 14 | * @inheritdoc 15 | */ 16 | public $modelClass = FieldRuleProperty::class; 17 | 18 | /** 19 | * @inheritdoc 20 | */ 21 | public $dataFile = __DIR__ . '/data/field_rule_property.php'; 22 | 23 | /** 24 | * @inheritdoc 25 | */ 26 | public $depends = [FieldRuleFixture::class]; 27 | } 28 | -------------------------------------------------------------------------------- /tests/_app/fixtures/FormFixture.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class FormFixture extends \yii\test\ActiveFixture 12 | { 13 | /** 14 | * @inheritdoc 15 | */ 16 | public $modelClass = Form::class; 17 | 18 | /** 19 | * @inheritdoc 20 | */ 21 | public $dataFile = __DIR__ . '/data/form.php'; 22 | } 23 | -------------------------------------------------------------------------------- /tests/_app/fixtures/OauthAccessTokensFixture.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class SectionFieldFixture extends \yii\test\ActiveFixture 12 | { 13 | /** 14 | * @inheritdoc 15 | */ 16 | public $modelClass = SectionField::class; 17 | 18 | /** 19 | * @inheritdoc 20 | */ 21 | public $dataFile = __DIR__ . '/data/section_field.php'; 22 | 23 | /** 24 | * @inheritdoc 25 | */ 26 | public $depends = [FieldFixture::class, SectionFixture::class]; 27 | } 28 | -------------------------------------------------------------------------------- /tests/_app/fixtures/SectionFixture.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class SectionFixture extends \yii\test\ActiveFixture 12 | { 13 | /** 14 | * @inheritdoc 15 | */ 16 | public $modelClass = Section::class; 17 | 18 | /** 19 | * @inheritdoc 20 | */ 21 | public $dataFile = __DIR__ . '/data/section.php'; 22 | /** 23 | * @inheritdoc 24 | */ 25 | public $depends = [FormFixture::class]; 26 | } 27 | -------------------------------------------------------------------------------- /tests/_app/fixtures/SolicitudeFixture.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class SolicitudeFixture extends \yii\test\ActiveFixture 12 | { 13 | /** 14 | * @inheritdoc 15 | */ 16 | public $modelClass = Solicitude::class; 17 | 18 | /** 19 | * @inheritdoc 20 | */ 21 | public $dataFile = __DIR__ . '/data/solicitude.php'; 22 | 23 | /** 24 | * @inhertidoc 25 | */ 26 | public $depends = [FormFixture::class]; 27 | } 28 | -------------------------------------------------------------------------------- /tests/_app/fixtures/SolicitudeValueFixture.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class SolicitudeValueFixture extends \yii\test\ActiveFixture 12 | { 13 | /** 14 | * @inheritdoc 15 | */ 16 | public $modelClass = SolicitudeValue::class; 17 | 18 | /** 19 | * @inheritdoc 20 | */ 21 | public $dataFile = __DIR__ . '/data/solicitude_value.php'; 22 | 23 | /** 24 | * @inheritdoc 25 | */ 26 | public $depends = [SolicitudeFixture::class, SectionFieldFixture::class]; 27 | } 28 | -------------------------------------------------------------------------------- /tests/_app/fixtures/UserFixture.php: -------------------------------------------------------------------------------- 1 | OauthAccessTokensFixture::SIMPLE_TOKEN, 9 | 'client_id' => 'testclient', 10 | 'user_id' => 1, 11 | 'expires' => new DbExpression('NOW() + INTERVAL 1 DAY'), 12 | ], 13 | ]; 14 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/clients.php: -------------------------------------------------------------------------------- 1 | 'testclient', 6 | 'client_secret' => 'testpass', 7 | 'redirect_uri' => 'http://fake/', 8 | 'grant_types' => 'client_credentials authorization_code ' 9 | . 'password implicit' 10 | ], 11 | ]; 12 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/field.php: -------------------------------------------------------------------------------- 1 | 1, 8 | 'name' => 'name', 9 | 'label' => 'Name(s)', 10 | 'created_by' => 1, 11 | 'created_at' => $now, 12 | 'updated_by' => 1, 13 | 'updated_at' => $now, 14 | ], 15 | [ 16 | 'data_type_id' => 1, 17 | 'name' => 'lastname', 18 | 'label' => 'Last Name(s)', 19 | 'created_by' => 1, 20 | 'created_at' => $now, 21 | 'updated_by' => 1, 22 | 'updated_at' => $now, 23 | ], 24 | [ 25 | 'data_type_id' => 1, 26 | 'name' => 'birthdate', 27 | 'label' => 'Date of Birth', 28 | 'created_by' => 1, 29 | 'created_at' => $now, 30 | 'updated_by' => 1, 31 | 'updated_at' => $now, 32 | ], 33 | [ 34 | 'data_type_id' => 1, 35 | 'name' => 'email', 36 | 'label' => 'Email', 37 | 'created_by' => 1, 38 | 'created_at' => $now, 39 | 'updated_by' => 1, 40 | 'updated_at' => $now, 41 | ], 42 | [ 43 | 'data_type_id' => 2, 44 | 'name' => 'country_id', 45 | 'label' => 'Country', 46 | 'service' => '/country', 47 | 'created_by' => 1, 48 | 'created_at' => $now, 49 | 'updated_by' => 1, 50 | 'updated_at' => $now, 51 | ], 52 | [ 53 | 'data_type_id' => 3, 54 | 'name' => 'income', 55 | 'label' => 'Income', 56 | 'created_by' => 1, 57 | 'created_at' => $now, 58 | 'updated_by' => 1, 59 | 'updated_at' => $now, 60 | ], 61 | ]; 62 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/field_rule.php: -------------------------------------------------------------------------------- 1 | 1, 8 | 'rule_class' => 'trim', 9 | 'created_by' => 1, 10 | 'created_at' => $now, 11 | 'updated_by' => 1, 12 | 'updated_at' => $now, 13 | ], 14 | [ 15 | 'field_id' => 1, 16 | 'rule_class' => 'required', 17 | 'created_by' => 1, 18 | 'created_at' => $now, 19 | 'updated_by' => 1, 20 | 'updated_at' => $now, 21 | ], 22 | [ 23 | 'field_id' => 1, 24 | 'rule_class' => 'string', 25 | 'created_by' => 1, 26 | 'created_at' => $now, 27 | 'updated_by' => 1, 28 | 'updated_at' => $now, 29 | ], 30 | [ 31 | 'field_id' => 2, 32 | 'rule_class' => 'trim', 33 | 'created_by' => 1, 34 | 'created_at' => $now, 35 | 'updated_by' => 1, 36 | 'updated_at' => $now, 37 | ], 38 | [ 39 | 'field_id' => 2, 40 | 'rule_class' => 'required', 41 | 'created_by' => 1, 42 | 'created_at' => $now, 43 | 'updated_by' => 1, 44 | 'updated_at' => $now, 45 | ], 46 | [ 47 | 'field_id' => 2, 48 | 'rule_class' => 'string', 49 | 'created_by' => 1, 50 | 'created_at' => $now, 51 | 'updated_by' => 1, 52 | 'updated_at' => $now, 53 | ], 54 | [ 55 | 'field_id' => 3, 56 | 'rule_class' => 'trim', 57 | 'created_by' => 1, 58 | 'created_at' => $now, 59 | 'updated_by' => 1, 60 | 'updated_at' => $now, 61 | ], 62 | [ 63 | 'field_id' => 3, 64 | 'rule_class' => 'required', 65 | 'created_by' => 1, 66 | 'created_at' => $now, 67 | 'updated_by' => 1, 68 | 'updated_at' => $now, 69 | ], 70 | [ 71 | 'field_id' => 3, 72 | 'rule_class' => 'date', 73 | 'created_by' => 1, 74 | 'created_at' => $now, 75 | 'updated_by' => 1, 76 | 'updated_at' => $now, 77 | ], 78 | [ 79 | 'field_id' => 4, 80 | 'rule_class' => 'trim', 81 | 'created_by' => 1, 82 | 'created_at' => $now, 83 | 'updated_by' => 1, 84 | 'updated_at' => $now, 85 | ], 86 | [ 87 | 'field_id' => 4, 88 | 'rule_class' => 'required', 89 | 'created_by' => 1, 90 | 'created_at' => $now, 91 | 'updated_by' => 1, 92 | 'updated_at' => $now, 93 | ], 94 | [ 95 | 'field_id' => 4, 96 | 'rule_class' => 'email', 97 | 'created_by' => 1, 98 | 'created_at' => $now, 99 | 'updated_by' => 1, 100 | 'updated_at' => $now, 101 | ], 102 | [ 103 | 'field_id' => 5, 104 | 'rule_class' => 'required', 105 | 'created_by' => 1, 106 | 'created_at' => $now, 107 | 'updated_by' => 1, 108 | 'updated_at' => $now, 109 | ], 110 | [ 111 | 'field_id' => 5, 112 | 'rule_class' => 'integer', 113 | 'created_by' => 1, 114 | 'created_at' => $now, 115 | 'updated_by' => 1, 116 | 'updated_at' => $now, 117 | ], 118 | [ 119 | 'field_id' => 6, 120 | 'rule_class' => 'number', 121 | 'created_by' => 1, 122 | 'created_at' => $now, 123 | 'updated_by' => 1, 124 | 'updated_at' => $now, 125 | ], 126 | ]; 127 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/field_rule_property.php: -------------------------------------------------------------------------------- 1 | 3, 8 | 'property' => 'min', 9 | 'value' => 6, 10 | 'created_by' => 1, 11 | 'created_at' => $now, 12 | 'updated_by' => 1, 13 | 'updated_at' => $now, 14 | ], 15 | [ 16 | 'rule_id' => 6, 17 | 'property' => 'min', 18 | 'value' => 6, 19 | 'created_by' => 1, 20 | 'created_at' => $now, 21 | 'updated_by' => 1, 22 | 'updated_at' => $now, 23 | ], 24 | [ 25 | 'rule_id' => 9, 26 | 'property' => 'format', 27 | 'value' => 'yyyy-MM-dd', 28 | 'created_by' => 1, 29 | 'created_at' => $now, 30 | 'updated_by' => 1, 31 | 'updated_at' => $now, 32 | ], 33 | [ 34 | 'rule_id' => 14, 35 | 'property' => 'min', 36 | 'value' => 1, 37 | 'created_by' => 1, 38 | 'created_at' => $now, 39 | 'updated_by' => 1, 40 | 'updated_at' => $now, 41 | ], 42 | [ 43 | 'rule_id' => 15, 44 | 'property' => 'min', 45 | 'value' => 0, 46 | 'created_by' => 1, 47 | 'created_at' => $now, 48 | 'updated_by' => 1, 49 | 'updated_at' => $now, 50 | ], 51 | ]; 52 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/form.php: -------------------------------------------------------------------------------- 1 | 'Participant Registration', 8 | 'created_by' => 1, 9 | 'created_at' => $now, 10 | 'updated_by' => 1, 11 | 'updated_at' => $now, 12 | ], 13 | ]; 14 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/section.php: -------------------------------------------------------------------------------- 1 | 1, 8 | 'position' => 1, 9 | 'name' => 'personal', 10 | 'label' => 'Personal Information', 11 | 'created_by' => 1, 12 | 'created_at' => $now, 13 | 'updated_by' => 1, 14 | 'updated_at' => $now, 15 | ], 16 | [ 17 | 'form_id' => 1, 18 | 'position' => 2, 19 | 'name' => 'professional', 20 | 'label' => 'Profesional Information', 21 | 'created_by' => 1, 22 | 'created_at' => $now, 23 | 'updated_by' => 1, 24 | 'updated_at' => $now, 25 | ], 26 | [ 27 | 'form_id' => 1, 28 | 'position' => 3, 29 | 'name' => 'credit', 30 | 'label' => 'Credit Information', 31 | 'created_by' => 1, 32 | 'created_at' => $now, 33 | 'updated_by' => 1, 34 | 'updated_at' => $now, 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/section_field.php: -------------------------------------------------------------------------------- 1 | 1, 8 | 'field_id' => 1, 9 | 'position' => 1, 10 | 'created_by' => 1, 11 | 'created_at' => $now, 12 | 'updated_by' => 1, 13 | 'updated_at' => $now, 14 | ], 15 | [ 16 | 'section_id' => 1, 17 | 'field_id' => 2, 18 | 'position' => 2, 19 | 'created_by' => 1, 20 | 'created_at' => $now, 21 | 'updated_by' => 1, 22 | 'updated_at' => $now, 23 | ], 24 | [ 25 | 'section_id' => 1, 26 | 'field_id' => 3, 27 | 'position' => 3, 28 | 'created_by' => 1, 29 | 'created_at' => $now, 30 | 'updated_by' => 1, 31 | 'updated_at' => $now, 32 | ], 33 | [ 34 | 'section_id' => 1, 35 | 'field_id' => 4, 36 | 'position' => 4, 37 | 'label' => 'Registration e-mail', 38 | 'created_by' => 1, 39 | 'created_at' => $now, 40 | 'updated_by' => 1, 41 | 'updated_at' => $now, 42 | ], 43 | [ 44 | 'section_id' => 2, 45 | 'field_id' => 5, 46 | 'position' => 1, 47 | 'label' => 'Country of residence', 48 | 'created_by' => 1, 49 | 'created_at' => $now, 50 | 'updated_by' => 1, 51 | 'updated_at' => $now, 52 | ], 53 | [ 54 | 'section_id' => 2, 55 | 'field_id' => 6, 56 | 'position' => 2, 57 | 'label' => 'Brute Income', 58 | 'created_by' => 1, 59 | 'created_at' => $now, 60 | 'updated_by' => 1, 61 | 'updated_at' => $now, 62 | ], 63 | ]; 64 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/solicitude.php: -------------------------------------------------------------------------------- 1 | 1, 8 | 'created_by' => 1, 9 | 'created_at' => $now, 10 | 'updated_by' => 1, 11 | 'updated_at' => $now, 12 | ], 13 | [ 14 | 'form_id' => 1, 15 | 'created_by' => 1, 16 | 'created_at' => $now, 17 | 'updated_by' => 1, 18 | 'updated_at' => $now, 19 | ], 20 | ]; 21 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/solicitude_value.php: -------------------------------------------------------------------------------- 1 | 1, 8 | 'section_id' => 1, 9 | 'field_id' => 1, 10 | 'value' => 'Manuel Angel', 11 | 'created_by' => 1, 12 | 'created_at' => $now, 13 | 'updated_by' => 1, 14 | 'updated_at' => $now, 15 | ], 16 | [ 17 | 'solicitude_id' => 1, 18 | 'section_id' => 1, 19 | 'field_id' => 2, 20 | 'value' => 'Guevara', 21 | 'created_by' => 1, 22 | 'created_at' => $now, 23 | 'updated_by' => 1, 24 | 'updated_at' => $now, 25 | ], 26 | [ 27 | 'solicitude_id' => 1, 28 | 'section_id' => 1, 29 | 'field_id' => 3, 30 | 'value' => '1980-01-01', 31 | 'created_by' => 1, 32 | 'created_at' => $now, 33 | 'updated_by' => 1, 34 | 'updated_at' => $now, 35 | ], 36 | [ 37 | 'solicitude_id' => 1, 38 | 'section_id' => 1, 39 | 'field_id' => 4, 40 | 'value' => 'aguevara@alquimiadigital.mx', 41 | 'created_by' => 1, 42 | 'created_at' => $now, 43 | 'updated_by' => 1, 44 | 'updated_at' => $now, 45 | ], 46 | [ 47 | 'solicitude_id' => 1, 48 | 'section_id' => 2, 49 | 'field_id' => 5, 50 | 'value' => 1, 51 | 'created_by' => 1, 52 | 'created_at' => $now, 53 | 'updated_by' => 1, 54 | 'updated_at' => $now, 55 | ], 56 | [ 57 | 'solicitude_id' => 1, 58 | 'section_id' => 2, 59 | 'field_id' => 6, 60 | 'value' => '1000.00', 61 | 'created_by' => 1, 62 | 'created_at' => $now, 63 | 'updated_by' => 1, 64 | 'updated_at' => $now, 65 | ], 66 | ]; 67 | -------------------------------------------------------------------------------- /tests/_app/fixtures/data/user.php: -------------------------------------------------------------------------------- 1 | 'erau', 6 | 'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI', 7 | // password_0 8 | 'password_hash' => '$2y$13$nJ1WDlBaGcbCdbNC5.5l4.sgy.OMEKCqtDQOdQ2OWpgiKRWYyzzne', 9 | 'password_reset_token' => 'RkD_Jw0_8HEedzLk7MM-ZKEFfYR7VbMr_1392559490', 10 | ], 11 | ]; 12 | -------------------------------------------------------------------------------- /tests/_app/index.php: -------------------------------------------------------------------------------- 1 | run(); 6 | -------------------------------------------------------------------------------- /tests/_app/migrations/m130101_000001_user.php: -------------------------------------------------------------------------------- 1 | $this->primaryKey(), 14 | 'username' => $this->string()->notNull()->unique(), 15 | 'auth_key' => $this->string(32)->notNull(), 16 | 'password_hash' => $this->string()->notNull(), 17 | 'password_reset_token' => $this->string()->unique(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/_app/models/User.php: -------------------------------------------------------------------------------- 1 | $id]); 24 | } 25 | 26 | /** 27 | * @inheritdoc 28 | */ 29 | public static function findIdentityByAccessToken($token, $type = null) 30 | { 31 | return static::find()->joinWith('accessTokens', false) 32 | ->andWhere(['access_token' => $token]) 33 | ->one(); 34 | } 35 | 36 | /** 37 | * Finds user by username 38 | * 39 | * @param string $username 40 | * @return static|null 41 | */ 42 | public static function findByUsername($username) 43 | { 44 | return static::findOne(['username' => $username]); 45 | } 46 | 47 | /** 48 | * Finds user by password reset token 49 | * 50 | * @param string $token password reset token 51 | * @return static|null 52 | */ 53 | public static function findByPasswordResetToken($token) 54 | { 55 | if (!static::isPasswordResetTokenValid($token)) { 56 | return null; 57 | } 58 | 59 | return static::findOne([ 60 | 'password_reset_token' => $token, 61 | 'status' => self::STATUS_ACTIVE, 62 | ]); 63 | } 64 | 65 | /** 66 | * Finds out if password reset token is valid 67 | * 68 | * @param string $token password reset token 69 | * @return bool 70 | */ 71 | public static function isPasswordResetTokenValid($token) 72 | { 73 | if (empty($token)) { 74 | return false; 75 | } 76 | 77 | $timestamp = (int) substr($token, strrpos($token, '_') + 1); 78 | $expire = Yii::$app->params['user.passwordResetTokenExpire']; 79 | 80 | return $timestamp + $expire >= time(); 81 | } 82 | 83 | /** 84 | * @inheritdoc 85 | */ 86 | public function getId() 87 | { 88 | return $this->getPrimaryKey(); 89 | } 90 | 91 | /** 92 | * @inheritdoc 93 | */ 94 | public function getAuthKey() 95 | { 96 | return $this->auth_key; 97 | } 98 | 99 | /** 100 | * @inheritdoc 101 | */ 102 | public function validateAuthKey($authKey) 103 | { 104 | return $this->getAuthKey() === $authKey; 105 | } 106 | 107 | /** 108 | * Validates password 109 | * 110 | * @param string $password password to validate 111 | * @return bool if password provided is valid for current user 112 | */ 113 | public function validatePassword($password) 114 | { 115 | return Yii::$app->security->validatePassword($password, $this->password_hash); 116 | } 117 | 118 | /** 119 | * Generates password hash from password and sets it to the model 120 | * 121 | * @param string $password 122 | */ 123 | public function setPassword($password) 124 | { 125 | $this->password_hash = Yii::$app->security->generatePasswordHash($password); 126 | } 127 | 128 | /** 129 | * Generates "remember me" authentication key 130 | */ 131 | public function generateAuthKey() 132 | { 133 | $this->auth_key = Yii::$app->security->generateRandomString(); 134 | } 135 | 136 | /** 137 | * Generates new password reset token 138 | */ 139 | public function generatePasswordResetToken() 140 | { 141 | $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time(); 142 | } 143 | 144 | /** 145 | * Removes password reset token 146 | */ 147 | public function removePasswordResetToken() 148 | { 149 | $this->password_reset_token = null; 150 | } 151 | 152 | /** 153 | * @inheritdoc 154 | */ 155 | public function checkUserCredentials($username, $password) 156 | { 157 | $user = static::findByUsername($username); 158 | if (empty($user)) { 159 | return false; 160 | } 161 | 162 | return $user->validatePassword($password); 163 | } 164 | 165 | /** 166 | * @inheritdoc 167 | */ 168 | public function getUserDetails($username = null) 169 | { 170 | $user = $username 171 | ? static::findByUsername($username) 172 | : $this; 173 | 174 | return ['user_id' => $user->id]; 175 | } 176 | 177 | /** 178 | * @return yii\db\ActiveQuery 179 | */ 180 | public function getAccessTokens() 181 | { 182 | return $this->hasMany(AccessToken::class, ['user_id' => 'id']) 183 | ->andOnCondition(['client_id' => 'testclient']); 184 | } 185 | 186 | 187 | public function rules() 188 | { 189 | return [ 190 | [['username'], 'string'], 191 | ]; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /tests/_app/views/layouts/main.php: -------------------------------------------------------------------------------- 1 | user->getIsGuest()) { 4 | echo \yii\helpers\Html::a('Login', ['/user/security/login']); 5 | echo \yii\helpers\Html::a('Registration', ['/user/registration/register']); 6 | } else { 7 | echo \yii\helpers\Html::a('Logout', ['/user/security/logout']); 8 | } 9 | 10 | echo $content; 11 | -------------------------------------------------------------------------------- /tests/_app/views/site/index.php: -------------------------------------------------------------------------------- 1 | Index 2 | -------------------------------------------------------------------------------- /tests/_app/yii.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); 12 | exit($exitCode); 13 | -------------------------------------------------------------------------------- /tests/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class FormCest extends \tecnocen\roa\test\AbstractResourceCest 14 | { 15 | protected function authToken(ApiTester $I) 16 | { 17 | $I->amBearerAuthenticated(OauthAccessTokensFixture::SIMPLE_TOKEN); 18 | } 19 | 20 | /** 21 | * @depends DataTypeCest:fixtures 22 | */ 23 | public function fixtures(ApiTester $I) 24 | { 25 | $I->haveFixtures([ 26 | 'form' => FormFixture::class, 27 | ]); 28 | } 29 | 30 | /** 31 | * @param ApiTester $I 32 | * @param Example $example 33 | * @dataprovider indexDataProvider 34 | * @depends fixtures 35 | * @before authToken 36 | */ 37 | public function index(ApiTester $I, Example $example) 38 | { 39 | $I->wantTo('Retrieve list of Form records.'); 40 | $this->internalIndex($I, $example); 41 | } 42 | 43 | /** 44 | * @return array for test `index()`. 45 | */ 46 | protected function indexDataProvider() 47 | { 48 | return [ 49 | 'list' => [ 50 | 'url' => '/form', 51 | 'httpCode' => HttpCode::OK, 52 | 'headers' => [ 53 | 'X-Pagination-Total-Count' => 1, 54 | ], 55 | ], 56 | 'not found form' => [ 57 | 'url' => '/form/15', 58 | 'httpCode' => HttpCode::NOT_FOUND, 59 | ], 60 | 'filter by author' => [ 61 | 'urlParams' => [ 62 | 'created_by' => 1, 63 | 'expand' => 'sections' 64 | ], 65 | 'httpCode' => HttpCode::OK, 66 | 'headers' => [ 67 | 'X-Pagination-Total-Count' => 1, 68 | ], 69 | ], 70 | 'filter by name' => [ 71 | 'urlParams' => [ 72 | 'name' => 'ticipa', 73 | ], 74 | 'httpCode' => HttpCode::OK, 75 | 'headers' => [ 76 | 'X-Pagination-Total-Count' => 1, 77 | ], 78 | ], 79 | 'rule created_by' => [ 80 | 'urlParams' => [ 81 | 'created_by' => 'wo', 82 | ], 83 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 84 | ], 85 | ]; 86 | } 87 | 88 | /** 89 | * @param ApiTester $I 90 | * @param Example $example 91 | * @dataprovider viewDataProvider 92 | * @depends fixtures 93 | * @before authToken 94 | */ 95 | public function view(ApiTester $I, Example $example) 96 | { 97 | $I->wantTo('Retrieve Form single record.'); 98 | $this->internalView($I, $example); 99 | } 100 | 101 | /** 102 | * @return array> data for test `view()`. 103 | */ 104 | protected function viewDataProvider() 105 | { 106 | return [ 107 | 'single record' => [ 108 | 'url' => '/form/1', 109 | 'data' => [ 110 | 'expand' => 'sections' 111 | ], 112 | 'httpCode' => HttpCode::OK, 113 | 'response' => [ 114 | '_embedded' => [ 115 | 'sections' => [ 116 | ['id' => 1], 117 | ], 118 | ], 119 | ], 120 | ], 121 | 'not found form record' => [ 122 | 'url' => '/form/8', 123 | 'httpCode' => HttpCode::NOT_FOUND, 124 | ], 125 | ]; 126 | } 127 | 128 | /** 129 | * @param ApiTester $I 130 | * @param Example $example 131 | * @dataprovider createDataProvider 132 | * @depends fixtures 133 | * @before authToken 134 | */ 135 | public function create(ApiTester $I, Example $example) 136 | { 137 | $I->wantTo('Create a Form record.'); 138 | $this->internalCreate($I, $example); 139 | } 140 | 141 | /** 142 | * @return array data for test `create()`. 143 | */ 144 | protected function createDataProvider() 145 | { 146 | return [ 147 | 'create form 1' => [ 148 | 'urlParams' => [ 149 | 'name' => 'First Math Test', 150 | ], 151 | 'httpCode' => HttpCode::CREATED, 152 | ], 153 | 'unique' => [ 154 | 'data' => ['name' => 'First Math Test'], 155 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 156 | 'validationErrors' => [ 157 | 'name' => 'Form name "First Math Test" has already been taken.' 158 | ], 159 | ], 160 | 'to short' => [ 161 | 'data' => ['name' => 'fo'], 162 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 163 | 'validationErrors' => [ 164 | 'name' => 'Form name should contain at least 6 characters.' 165 | ], 166 | ], 167 | 'not blank' => [ 168 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 169 | 'validationErrors' => [ 170 | 'name' => 'Form name cannot be blank.' 171 | ], 172 | ], 173 | ]; 174 | } 175 | 176 | /** 177 | * @param ApiTester $I 178 | * @param Example $example 179 | * @dataprovider updateDataProvider 180 | * @depends fixtures 181 | * @before authToken 182 | */ 183 | public function update(ApiTester $I, Example $example) 184 | { 185 | $I->wantTo('Update a Form record.'); 186 | $this->internalUpdate($I, $example); 187 | } 188 | 189 | /** 190 | * @return array[] data for test `update()`. 191 | */ 192 | protected function updateDataProvider() 193 | { 194 | return [ 195 | 'update form 1' => [ 196 | 'urlParams' => ['id' => '1'], 197 | 'data' => ['name' => 'Second Math Test'], 198 | 'httpCode' => HttpCode::OK, 199 | ], 200 | 'to short' => [ 201 | 'urlParams' => ['id' => '1'], 202 | 'data' => ['name' => 'fo'], 203 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 204 | 'validationErrors' => [ 205 | 'name' => 'Form name should contain at least 6 characters.' 206 | ], 207 | ], 208 | ]; 209 | } 210 | 211 | /** 212 | * @param ApiTester $I 213 | * @param Example $example 214 | * @dataprovider deleteDataProvider 215 | * @depends fixtures 216 | * @before authToken 217 | */ 218 | public function delete(ApiTester $I, Example $example) 219 | { 220 | $I->wantTo('Delete a Form record.'); 221 | $this->internalDelete($I, $example); 222 | } 223 | 224 | /** 225 | * @return array[] data for test `delete()`. 226 | */ 227 | protected function deleteDataProvider() 228 | { 229 | return [ 230 | 'delete form 1' => [ 231 | 'urlParams' => ['id' => '1'], 232 | 'httpCode' => HttpCode::NO_CONTENT, 233 | ], 234 | 'not found' => [ 235 | 'urlParams' => ['id' => '1'], 236 | 'httpCode' => HttpCode::NOT_FOUND, 237 | 'validationErrors' => [ 238 | 'name' => 'The record "1" does not exists.' 239 | ], 240 | ], 241 | ]; 242 | } 243 | 244 | /** 245 | * @inheritdoc 246 | */ 247 | protected function recordJsonType() 248 | { 249 | return [ 250 | 'id' => 'integer:>0', 251 | 'name' => 'string', 252 | ]; 253 | } 254 | 255 | /** 256 | * @inheritdoc 257 | */ 258 | protected function getRoutePattern() 259 | { 260 | return 'form'; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /tests/api/SearchValueCest.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class SearchValueCest extends \tecnocen\roa\test\AbstractResourceCest 14 | { 15 | protected function authToken(ApiTester $I) 16 | { 17 | $I->amBearerAuthenticated(OauthAccessTokensFixture::SIMPLE_TOKEN); 18 | } 19 | 20 | public function fixtures(ApiTester $I) 21 | { 22 | $I->haveFixtures([ 23 | 'access_tokens' => OauthAccessTokensFixture::class, 24 | 'solicitude' => SolicitudeValueFixture::class, 25 | ]); 26 | } 27 | 28 | /** 29 | * @param ApiTester $I 30 | * @param Example $example 31 | * @dataprovider indexDataProvider 32 | * @depends fixtures 33 | * @before authToken 34 | */ 35 | public function index(ApiTester $I, Example $example) 36 | { 37 | $I->wantTo('Retrieve list of SolicitudeValue records.'); 38 | $this->internalIndex($I, $example); 39 | } 40 | 41 | /** 42 | * @return array for test `index()`. 43 | */ 44 | protected function indexDataProvider() 45 | { 46 | return [ 47 | 'single value' => [ 48 | 'urlParams' => [ 49 | 'value' => 'guevara', 50 | ], 51 | 'httpCode' => HttpCode::OK, 52 | 'headers' => [ 53 | 'X-Pagination-Total-Count' => 2, 54 | ], 55 | ], 56 | 'invalid search' => [ 57 | 'urlParams' => [ 58 | 'form_id' => 'foo', 59 | ], 60 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 61 | ], 62 | ]; 63 | } 64 | 65 | /** 66 | * @inheritdoc 67 | */ 68 | protected function recordJsonType() 69 | { 70 | return [ 71 | 'solicitude_id' => 'integer:>0', 72 | 'section_id' => 'integer:>0', 73 | 'field_id' => 'integer:>0', 74 | 'value' => 'string', 75 | ]; 76 | } 77 | 78 | /** 79 | * @inheritdoc 80 | */ 81 | protected function getRoutePattern() 82 | { 83 | return 'solicitude-value'; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/api/_bootstrap.php: -------------------------------------------------------------------------------- 1 | /rule resource. 13 | * 14 | * @author Carlos (neverabe) Llamosas 15 | */ 16 | class FieldRuleCest extends \tecnocen\roa\test\AbstractResourceCest 17 | { 18 | protected function authToken(ApiTester $I) 19 | { 20 | $I->amBearerAuthenticated(OauthAccessTokensFixture::SIMPLE_TOKEN); 21 | } 22 | 23 | /** 24 | * @depends FieldCest:fixtures 25 | */ 26 | public function fixtures(ApiTester $I) 27 | { 28 | $I->haveFixtures([ 29 | 'access_tokens' => OauthAccessTokensFixture::class, 30 | 'field_rule' => FieldRuleFixture::class, 31 | ]); 32 | } 33 | 34 | /** 35 | * @param ApiTester $I 36 | * @param Example $example 37 | * @dataprovider indexDataProvider 38 | * @depends fixtures 39 | * @before authToken 40 | */ 41 | public function index(ApiTester $I, Example $example) 42 | { 43 | $I->wantTo('Retrieve list of FieldRule records.'); 44 | $this->internalIndex($I, $example); 45 | } 46 | 47 | /** 48 | * @return array for test `index()`. 49 | */ 50 | protected function indexDataProvider() 51 | { 52 | return [ 53 | 'list' => [ 54 | 'urlParams' => [ 55 | 'field_id' => 3, 56 | 'expand' => 'properties' 57 | ], 58 | 'httpCode' => HttpCode::OK, 59 | 'headers' => [ 60 | 'X-Pagination-Total-Count' => 3, 61 | ], 62 | ], 63 | 'not found field' => [ 64 | 'url' => '/field/15', 65 | 'httpCode' => HttpCode::NOT_FOUND, 66 | ], 67 | 'filter by author' => [ 68 | 'urlParams' => [ 69 | 'field_id' => 1, 70 | 'created_by' => 1, 71 | 'expand' => 'properties' 72 | ], 73 | 'httpCode' => HttpCode::OK, 74 | 'headers' => [ 75 | 'X-Pagination-Total-Count' => 3, 76 | ], 77 | ], 78 | 'filter by name' => [ 79 | 'urlParams' => [ 80 | 'field_id' => 1, 81 | 'rule_class' => 'string', 82 | ], 83 | 'httpCode' => HttpCode::OK, 84 | 'headers' => [ 85 | 'X-Pagination-Total-Count' => 1, 86 | ], 87 | ], 88 | ]; 89 | } 90 | 91 | /** 92 | * @param ApiTester $I 93 | * @param Example $example 94 | * @dataprovider viewDataProvider 95 | * @depends fixtures 96 | * @before authToken 97 | */ 98 | public function view(ApiTester $I, Example $example) 99 | { 100 | $I->wantTo('Retrieve FieldRule single record.'); 101 | $this->internalView($I, $example); 102 | } 103 | 104 | /** 105 | * @return array> data for test `view()`. 106 | */ 107 | protected function viewDataProvider() 108 | { 109 | return [ 110 | 'single record' => [ 111 | 'url' => '/field/1/rule/1', 112 | 'data' => [ 113 | 'expand' => 'field, properties', 114 | ], 115 | 'httpCode' => HttpCode::OK, 116 | ], 117 | 'not found field record' => [ 118 | 'url' => '/field/8/rule/1', 119 | 'httpCode' => HttpCode::NOT_FOUND, 120 | ], 121 | 'not found rule record' => [ 122 | 'url' => '/field/1/rule/10', 123 | 'httpCode' => HttpCode::NOT_FOUND, 124 | ], 125 | ]; 126 | } 127 | 128 | /** 129 | * @param ApiTester $I 130 | * @param Example $example 131 | * @dataprovider createDataProvider 132 | * @depends fixtures 133 | * @before authToken 134 | */ 135 | public function create(ApiTester $I, Example $example) 136 | { 137 | $I->wantTo('Create a FieldRule record.'); 138 | $this->internalCreate($I, $example); 139 | } 140 | 141 | /** 142 | * @return array data for test `create()`. 143 | */ 144 | protected function createDataProvider() 145 | { 146 | return [ 147 | 'create field 1' => [ 148 | 'urlParams' => [ 149 | 'field_id' => 1, 150 | ], 151 | 'data' => ['rule_class' => 'string'], 152 | 'httpCode' => HttpCode::CREATED, 153 | ], 154 | ]; 155 | } 156 | 157 | /** 158 | * @param ApiTester $I 159 | * @param Example $example 160 | * @dataprovider updateDataProvider 161 | * @depends fixtures 162 | * @before authToken 163 | */ 164 | public function update(ApiTester $I, Example $example) 165 | { 166 | $I->wantTo('Update a FieldRule record.'); 167 | $this->internalUpdate($I, $example); 168 | } 169 | 170 | /** 171 | * @return array[] data for test `update()`. 172 | */ 173 | protected function updateDataProvider() 174 | { 175 | return [ 176 | 'update field rule 1' => [ 177 | 'urlParams' => [ 178 | 'field_id' => 1, 179 | 'id' => '1', 180 | ], 181 | 'httpCode' => HttpCode::METHOD_NOT_ALLOWED, 182 | 'data' => [], 183 | ], 184 | ]; 185 | } 186 | 187 | /** 188 | * @param ApiTester $I 189 | * @param Example $example 190 | * @dataprovider deleteDataProvider 191 | * @depends fixtures 192 | * @before authToken 193 | */ 194 | public function delete(ApiTester $I, Example $example) 195 | { 196 | $I->wantTo('Delete a FieldRule record.'); 197 | $this->internalDelete($I, $example); 198 | } 199 | 200 | /** 201 | * @return array[] data for test `delete()`. 202 | */ 203 | protected function deleteDataProvider() 204 | { 205 | return [ 206 | 'delete field rule 1' => [ 207 | 'urlParams' => [ 208 | 'field_id' => 1, 209 | 'id' => 16, 210 | ], 211 | 'httpCode' => HttpCode::NO_CONTENT, 212 | ], 213 | 'not found' => [ 214 | 'urlParams' => [ 215 | 'field_id' => 1, 216 | 'id' => 16, 217 | ], 218 | 'httpCode' => HttpCode::NOT_FOUND, 219 | ], 220 | ]; 221 | } 222 | 223 | /** 224 | * @inheritdoc 225 | */ 226 | protected function recordJsonType() 227 | { 228 | return [ 229 | 'id' => 'integer:>0', 230 | 'field_id' => 'integer:>0', 231 | 'rule_class' => 'string' 232 | ]; 233 | } 234 | 235 | /** 236 | * @inheritdoc 237 | */ 238 | protected function getRoutePattern() 239 | { 240 | return 'field//rule'; 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /tests/api/field/rule/FieldRulePropertyCest.php: -------------------------------------------------------------------------------- 1 | /rule//property resource. 13 | * 14 | * @author Carlos (neverabe) Llamosas 15 | */ 16 | class FieldRulePropertyCest extends \tecnocen\roa\test\AbstractResourceCest 17 | { 18 | protected function authToken(ApiTester $I) 19 | { 20 | $I->amBearerAuthenticated(OauthAccessTokensFixture::SIMPLE_TOKEN); 21 | } 22 | 23 | /** 24 | * @depends field\FieldRuleCest:fixtures 25 | */ 26 | public function fixtures(ApiTester $I) 27 | { 28 | $I->haveFixtures([ 29 | 'field_rule_property' => [ 30 | 'class' =>FieldRulePropertyFixture::class, 31 | 'depends' => [], 32 | ], 33 | ]); 34 | } 35 | 36 | /** 37 | * @param ApiTester $I 38 | * @param Example $example 39 | * @dataprovider indexDataProvider 40 | * @depends fixtures 41 | * @before authToken 42 | */ 43 | public function index(ApiTester $I, Example $example) 44 | { 45 | $I->wantTo('Retrieve list of FieldRuleProperty records.'); 46 | $this->internalIndex($I, $example); 47 | } 48 | 49 | /** 50 | * @return array for test `index()`. 51 | */ 52 | protected function indexDataProvider() 53 | { 54 | return [ 55 | 'list' => [ 56 | 'urlParams' => [ 57 | 'field_id' => 1, 58 | 'rule_id' => 3, 59 | ], 60 | 'httpCode' => HttpCode::OK, 61 | 'headers' => [ 62 | 'X-Pagination-Total-Count' => 1, 63 | ], 64 | ], 65 | 'not found field' => [ 66 | 'urlParams' => [ 67 | 'field_id' => 250, 68 | 'rule_id' => 3, 69 | ], 70 | 'httpCode' => HttpCode::NOT_FOUND, 71 | ], 72 | 'not found rule' => [ 73 | 'urlParams' => [ 74 | 'field_id' => 1, 75 | 'rule_id' => 250, 76 | ], 77 | 'httpCode' => HttpCode::NOT_FOUND, 78 | ], 79 | 'filter by author' => [ 80 | 'urlParams' => [ 81 | 'field_id' => 1, 82 | 'rule_id' => 3, 83 | 'created_by' => 1, 84 | ], 85 | 'httpCode' => HttpCode::OK, 86 | 'headers' => [ 87 | 'X-Pagination-Total-Count' => 1, 88 | ], 89 | ], 90 | 'invalid author' => [ 91 | 'urlParams' => [ 92 | 'field_id' => 1, 93 | 'rule_id' => 3, 94 | 'created_by' => 'foo', 95 | ], 96 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 97 | ], 98 | ]; 99 | } 100 | 101 | /** 102 | * @param ApiTester $I 103 | * @param Example $example 104 | * @dataprovider viewDataProvider 105 | * @depends fixtures 106 | * @before authToken 107 | */ 108 | public function view(ApiTester $I, Example $example) 109 | { 110 | $I->wantTo('Retrieve FieldRuleProperty single record.'); 111 | $this->internalView($I, $example); 112 | } 113 | 114 | /** 115 | * @return array> data for test `view()`. 116 | */ 117 | protected function viewDataProvider() 118 | { 119 | return [ 120 | 'single record' => [ 121 | 'url' => '/field/1/rule/3/property/min?expand=rule', 122 | 'httpCode' => HttpCode::OK, 123 | ], 124 | 'not found field record' => [ 125 | 'url' => '/field/250/rule/3/property/min?expand=rule', 126 | 'httpCode' => HttpCode::NOT_FOUND, 127 | ], 128 | 'not found rule record' => [ 129 | 'url' => '/field/1/rule/3/property/asdf?expand=rule', 130 | 'httpCode' => HttpCode::NOT_FOUND, 131 | ], 132 | ]; 133 | } 134 | 135 | /** 136 | * @param ApiTester $I 137 | * @param Example $example 138 | * @dataprovider createDataProvider 139 | * @depends fixtures 140 | * @before authToken 141 | */ 142 | public function create(ApiTester $I, Example $example) 143 | { 144 | $I->wantTo('Create a FieldRuleProperty record.'); 145 | $this->internalCreate($I, $example); 146 | } 147 | 148 | /** 149 | * @return array data for test `create()`. 150 | */ 151 | protected function createDataProvider() 152 | { 153 | return [ 154 | 'create field 1' => [ 155 | 'urlParams' => [ 156 | 'field_id' => 1, 157 | 'rule_id' => 3, 158 | 'property' => 'max', 159 | 'value' => 50, 160 | ], 161 | 'httpCode' => HttpCode::CREATED, 162 | ], 163 | 'unique and invalid id' => [ 164 | 'urlParams' => [ 165 | 'field_id' => 1, 166 | 'rule_id' => 3, 167 | 'property' => 'max', 168 | 'value' => 50 169 | ], 170 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 171 | 'validationErrors' => [ 172 | 'property' => 'Property already in use for this rule.', 173 | ], 174 | ], 175 | 'not blank' => [ 176 | 'urlParams' => [ 177 | 'field_id' => 1, 178 | 'rule_id' => 3, 179 | 'property' => '', 180 | 'value' => 50 181 | ], 182 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 183 | 'validationErrors' => [ 184 | 'property' => 'Property cannot be blank.', 185 | ], 186 | ], 187 | ]; 188 | } 189 | 190 | /** 191 | * @param ApiTester $I 192 | * @param Example $example 193 | * @dataprovider updateDataProvider 194 | * @depends fixtures 195 | * @before authToken 196 | */ 197 | public function update(ApiTester $I, Example $example) 198 | { 199 | $I->wantTo('Update a FieldRuleProperty record.'); 200 | $this->internalUpdate($I, $example); 201 | } 202 | 203 | /** 204 | * @return array[] data for test `update()`. 205 | */ 206 | protected function updateDataProvider() 207 | { 208 | return [ 209 | 'update field 1' => [ 210 | 'url' => '/field/1/rule/3/property/max', 211 | 'data' => [ 212 | 'value' => 60 213 | ], 214 | 'httpCode' => HttpCode::OK, 215 | ] 216 | ]; 217 | } 218 | 219 | /** 220 | * @param ApiTester $I 221 | * @param Example $example 222 | * @dataprovider deleteDataProvider 223 | * @depends fixtures 224 | * @before authToken 225 | */ 226 | public function delete(ApiTester $I, Example $example) 227 | { 228 | $I->wantTo('Delete a FieldRuleProperty record.'); 229 | $this->internalDelete($I, $example); 230 | } 231 | 232 | /** 233 | * @return array[] data for test `delete()`. 234 | */ 235 | protected function deleteDataProvider() 236 | { 237 | return [ 238 | 'delete field 1' => [ 239 | 'url' => '/field/1/rule/3/property/max', 240 | 'httpCode' => HttpCode::NO_CONTENT, 241 | ], 242 | 'not found' => [ 243 | 'url' => '/field/1/rule/3/property/max', 244 | 'httpCode' => HttpCode::NOT_FOUND, 245 | ], 246 | ]; 247 | } 248 | 249 | /** 250 | * @inheritdoc 251 | */ 252 | protected function recordJsonType() 253 | { 254 | return [ 255 | 'property' => 'string', 256 | 'value' => 'string' 257 | ]; 258 | } 259 | 260 | /** 261 | * @inheritdoc 262 | */ 263 | protected function getRoutePattern() 264 | { 265 | return 'field//rule//property'; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /tests/api/form/SolicitudeCest.php: -------------------------------------------------------------------------------- 1 | /solicitude resource. 10 | * 11 | * @author Carlos (neverabe) Llamosas 12 | */ 13 | class SolicitudeCest extends \tecnocen\roa\test\AbstractResourceCest 14 | { 15 | protected function authToken(ApiTester $I) 16 | { 17 | $I->amBearerAuthenticated(OauthAccessTokensFixture::SIMPLE_TOKEN); 18 | } 19 | 20 | public function fixtures(ApiTester $I) 21 | { 22 | $I->haveFixtures([ 23 | 'access_tokens' => OauthAccessTokensFixture::class, 24 | 'solicitude' => SolicitudeFixture::class, 25 | ]); 26 | } 27 | 28 | /** 29 | * @param ApiTester $I 30 | * @param Example $example 31 | * @dataprovider indexDataProvider 32 | * @depends fixtures 33 | * @before authToken 34 | */ 35 | public function index(ApiTester $I, Example $example) 36 | { 37 | $I->wantTo('Retrieve list of Solicitude records.'); 38 | $this->internalIndex($I, $example); 39 | } 40 | 41 | /** 42 | * @return array for test `index()`. 43 | */ 44 | protected function indexDataProvider() 45 | { 46 | return [ 47 | 'list' => [ 48 | 'urlParams' => [ 49 | 'form_id' => 1, 50 | ], 51 | 'httpCode' => HttpCode::OK, 52 | 'headers' => [ 53 | 'X-Pagination-Total-Count' => 2, 54 | ], 55 | ], 56 | 'not found form' => [ 57 | 'urlParams' => [ 58 | 'form_id' => 15, 59 | ], 60 | 'httpCode' => HttpCode::NOT_FOUND, 61 | ], 62 | 'filter by author' => [ 63 | 'urlParams' => [ 64 | 'form_id' => 1, 65 | 'created_by' => 1, 66 | ], 67 | 'httpCode' => HttpCode::OK, 68 | 'headers' => [ 69 | 'X-Pagination-Total-Count' => 2, 70 | ], 71 | ], 72 | 'filter by values' => [ 73 | 'urlParams' => [ 74 | 'form_id' => 1, 75 | 'value' => [1 => 'Manuel'], 76 | ], 77 | 'httpCode' => HttpCode::OK, 78 | 'headers' => [ 79 | 'X-Pagination-Total-Count' => 1, 80 | ], 81 | ], 82 | 'rule created_by' => [ 83 | 'urlParams' => [ 84 | 'form_id' => 1, 85 | 'created_by' => 'wo', 86 | ], 87 | 'httpCode' => HttpCode::UNPROCESSABLE_ENTITY, 88 | ], 89 | ]; 90 | } 91 | 92 | /** 93 | * @param ApiTester $I 94 | * @param Example $example 95 | * @dataprovider viewDataProvider 96 | * @depends fixtures 97 | * @before authToken 98 | */ 99 | public function view(ApiTester $I, Example $example) 100 | { 101 | $I->wantTo('Retrieve Solicitude single record.'); 102 | $this->internalView($I, $example); 103 | } 104 | 105 | /** 106 | * @return array> data for test `view()`. 107 | */ 108 | protected function viewDataProvider() 109 | { 110 | return [ 111 | 'single record' => [ 112 | 'url' => '/form/1/solicitude', 113 | 'data' => [ 114 | 'expand' => 'form, values', 115 | ], 116 | 'httpCode' => HttpCode::OK, 117 | ], 118 | 'not found form and solicitude' => [ 119 | 'urlParams' => [ 120 | 'form_id' => 10, 121 | 'id' => 10, 122 | 'expand' => 'solicitudes' 123 | ], 124 | 'httpCode' => HttpCode::NOT_FOUND, 125 | ], 126 | 'not found form record' => [ 127 | 'urlParams' => [ 128 | 'form_id' => 10, 129 | 'id' => 1, 130 | 'expand' => 'solicitudes' 131 | ], 132 | 'httpCode' => HttpCode::NOT_FOUND, 133 | ], 134 | 'not found solicitude record' => [ 135 | 'urlParams' => [ 136 | 'form_id' => 1, 137 | 'id' => 10, 138 | 'expand' => 'solicitudes' 139 | ], 140 | 'httpCode' => HttpCode::NOT_FOUND, 141 | ], 142 | ]; 143 | } 144 | 145 | /** 146 | * @param ApiTester $I 147 | * @param Example $example 148 | * @dataprovider createDataProvider 149 | * @depends fixtures 150 | * @before authToken 151 | */ 152 | public function create(ApiTester $I, Example $example) 153 | { 154 | $I->wantTo('Create a Solicitude record.'); 155 | $this->internalCreate($I, $example); 156 | } 157 | 158 | /** 159 | * @return array data for test `create()`. 160 | */ 161 | protected function createDataProvider() 162 | { 163 | return [ 164 | 'create form 1' => [ 165 | 'urlParams' => [ 166 | 'form_id' => 1, 167 | ], 168 | 'httpCode' => HttpCode::CREATED, 169 | ], 170 | ]; 171 | } 172 | 173 | /** 174 | * @param ApiTester $I 175 | * @param Example $example 176 | * @dataprovider updateDataProvider 177 | * @depends fixtures 178 | * @before authToken 179 | */ 180 | public function update(ApiTester $I, Example $example) 181 | { 182 | $I->wantTo('Update a Solicitude record.'); 183 | $this->internalUpdate($I, $example); 184 | } 185 | 186 | /** 187 | * @return array[] data for test `update()`. 188 | */ 189 | protected function updateDataProvider() 190 | { 191 | return [ 192 | 'update form 1' => [ 193 | 'url' => '/form/1/solicitude/1', 194 | 'data' => [ 195 | 'name' => 'asignatures2', 196 | 'label' => 'Asignatures 2', 197 | ], 198 | 'httpCode' => HttpCode::OK, 199 | ], 200 | ]; 201 | } 202 | 203 | /** 204 | * @param ApiTester $I 205 | * @param Example $example 206 | * @dataprovider deleteDataProvider 207 | * @depends fixtures 208 | * @before authToken 209 | */ 210 | public function delete(ApiTester $I, Example $example) 211 | { 212 | $I->wantTo('Delete a Solicitude record.'); 213 | $this->internalDelete($I, $example); 214 | } 215 | 216 | /** 217 | * @return array[] data for test `delete()`. 218 | */ 219 | protected function deleteDataProvider() 220 | { 221 | return [ 222 | 'delete form 1' => [ 223 | 'urlParams' => [ 224 | 'form_id' => 1, 225 | 'id' => 1, 226 | ], 227 | 'httpCode' => HttpCode::NO_CONTENT, 228 | ], 229 | 'not found' => [ 230 | 'urlParams' => [ 231 | 'form_id' => 1, 232 | 'id' => 1, 233 | ], 234 | 'httpCode' => HttpCode::NOT_FOUND, 235 | 'validationErrors' => [ 236 | 'name' => 'The record "1" does not exists.' 237 | ], 238 | ], 239 | ]; 240 | } 241 | 242 | /** 243 | * @inheritdoc 244 | */ 245 | protected function recordJsonType() 246 | { 247 | return [ 248 | 'id' => 'integer:>0', 249 | 'form_id' => 'integer:>0', 250 | ]; 251 | } 252 | 253 | /** 254 | * @inheritdoc 255 | */ 256 | protected function getRoutePattern() 257 | { 258 | return 'form//solicitude'; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /tests/unit.suite.yml: -------------------------------------------------------------------------------- 1 | class_name: UnitTester 2 | modules: 3 | enabled: 4 | - Asserts 5 | - Yii2: 6 | cleanup: false -------------------------------------------------------------------------------- /tests/unit/_bootstrap.php: -------------------------------------------------------------------------------- 1 | expectException( 16 | new InvalidConfigException( 17 | Positionable::class . '::$parentAttribute must be set.' 18 | ), 19 | function () { 20 | $model = new Component(); 21 | $model->attachBehavior('positionable', new Positionable()); 22 | } 23 | ); 24 | 25 | $I->expectException( 26 | new InvalidConfigException( 27 | Positionable::class 28 | . '::$owner must extend ' 29 | . ActiveRecord::class 30 | ), 31 | function () { 32 | $model = new Component(); 33 | $model->attachBehavior('positionable', new Positionable([ 34 | 'parentAttribute' => 'none', 35 | ])); 36 | } 37 | ); 38 | 39 | $I->expectException( 40 | new InvalidConfigException( 41 | Form::class . '::$none is not an attribute.' 42 | ), 43 | function () { 44 | $model = new Form(); 45 | $model->attachBehavior('positionable', new Positionable([ 46 | 'parentAttribute' => 'none', 47 | ])); 48 | } 49 | ); 50 | 51 | $I->expectException( 52 | new InvalidConfigException( 53 | Form::class . '::$position is not an attribute.' 54 | ), 55 | function () { 56 | $model = new Form(); 57 | $model->attachBehavior('positionable', new Positionable([ 58 | 'parentAttribute' => 'id', 59 | ])); 60 | } 61 | ); 62 | } 63 | } 64 | --------------------------------------------------------------------------------