{"id":18973698,"url":"https://github.com/64robots/webforms","last_synced_at":"2025-10-12T09:05:54.294Z","repository":{"id":49091718,"uuid":"279892641","full_name":"64robots/webforms","owner":"64robots","description":"Webforms backend","archived":false,"fork":false,"pushed_at":"2021-06-29T12:53:44.000Z","size":558,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-09-28T23:44:29.033Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://webforms.64robots.com","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/64robots.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null}},"created_at":"2020-07-15T14:36:02.000Z","updated_at":"2021-06-30T20:22:39.000Z","dependencies_parsed_at":"2022-09-22T14:26:08.084Z","dependency_job_id":null,"html_url":"https://github.com/64robots/webforms","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/64robots/webforms","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/64robots%2Fwebforms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/64robots%2Fwebforms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/64robots%2Fwebforms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/64robots%2Fwebforms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/64robots","download_url":"https://codeload.github.com/64robots/webforms/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/64robots%2Fwebforms/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279010951,"owners_count":26084835,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T15:12:51.701Z","updated_at":"2025-10-12T09:05:54.267Z","avatar_url":"https://github.com/64robots.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Backend for 64 Robots webforms\n\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/64robots/webforms.svg?style=flat-square)](https://packagist.org/packages/64robots/webforms)\n[![MIT Licensed](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)\n[![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/64robots/webforms/run-tests?label=tests)](https://github.com/64robots/webforms/actions?query=workflow%3Arun-tests+branch%3Amaster)\n[![Total Downloads](https://img.shields.io/packagist/dt/64robots/webforms.svg?style=flat-square)](https://packagist.org/packages/64robots/webforms)\n\nPackage to rapidly create custom forms. This package provides you an easy way to start the backend for an SPA Form. You could create forms, form steps and questions. Your users could respond to these forms. Made by [64 Robots](https://64robots.com).\n\n## Installation\n\nYou can install the package via composer:\n\n```bash\ncomposer require 64robots/webforms\n```\n\nYou can publish and run the migrations with:\n\n```bash\nphp artisan vendor:publish --provider=\"R64\\Webforms\\WebformsServiceProvider\" --tag=\"migrations\"\nphp artisan migrate\n```\n\nYou can publish the config file with:\n```bash\nphp artisan vendor:publish --provider=\"R64\\Webforms\\WebformsServiceProvider\" --tag=\"config\"\n```\n\nThis is the contents of the published `webforms.php` config file:\n\n```php\nuse R64\\Webforms\\QuestionTypes\\EmailType;\nuse R64\\Webforms\\QuestionTypes\\PhoneType;\n\nreturn [\n    'date_format' =\u003e 'Y-m-d',\n    'year_month_format' =\u003e 'Y-m',\n    'fields_to_be_confirmed' =\u003e [\n        EmailType::TYPE,\n        PhoneType::TYPE,\n    ],\n    'user_model' =\u003e 'App\\User',\n];\n```\n\n## Usage\n\n1 - Add Routes\n\nAt the moment, the package doesn't work with anonymous users. Please, add that to your routes file under an auth middleware:\n\n```php\nRoute::webforms('webforms');\n```\n\nIf you want routes to create Forms, FormSteps and Questions, add that under the appropriate middleware in your routes file:\n\n```php\nRoute::webformsAdmin('webforms-admin');\n```\n\n2 - Add `HasWebForms` trait in your user entity.\n\n3 - Create Seeders for Form, FormSteps and Question.\n\n## Example\n\nLet's start a new form to collect info about coffee in your app:\n\nWe will add in `routes/api.php`:\n\n```php\nRoute::webforms('webforms');\n```\n\nWe will also add in `routes/api_admin.php`:\n\n```php\nRoute::webformsAdmin('webforms-admin');\n```\n\nThe next step is to add a new Form. Just create a new Seeder.\n\n```\nphp artisan make:seeder CoffeeSeeder\n```\n\nNow in the Seeder file `database/seeders/CoffeeSeeder` we'll include the creation of the form, form steps and questions.\n\nLet's start with the form creation:\n\n```php\nuse R64\\Webforms\\Models\\Form;\n\n$coffeeForm = Form::build('Coffee Form')\n    -\u003esave();\n```\n\nOnce we have the `form` we'll need to add, at least, a form step:\n\n```php\nuse R64\\Webforms\\Models\\FormStep;\n\n$coffeeStep = FormStep::build($coffeeForm, 'Coffee')\n    -\u003esave();\n```\n\nThen we can add `questions` to this step:\n\n```php\nuse R64\\Webforms\\Models\\Question;\nuse R64\\Webforms\\QuestionTypes\\OptionsType;\n\n$whatKindOfCoffeeDoYouLikeQuestion = Question::build($coffeeStep, 'What kind of coffee do you like?')\n    -\u003etype(OptionsType::TYPE)\n    -\u003eoptions([\n        'black' =\u003e 'Black', \n        'latte' =\u003e 'Latte',\n        'capuccino' =\u003e 'Cappucino',\n        'americano' =\u003e 'Americano',\n        'red-eye' =\u003e 'Red Eye',\n        'flat-white' =\u003e 'Flat White',\n     ])\n    -\u003esave();\n\n$whatTypeOfBeansDoYouLikeQuestion = Question::build($coffeeStep, 'What type of coffee beans do you like the most?')\n    -\u003etype(OptionsType::TYPE)\n    -\u003eoptions([\n        'arabica' =\u003e 'Arabica',\n        'robusta' =\u003e 'Robusta',\n    ])\n    -\u003esave();\n```\n\nAdd now a new step to collect some personal info. We'll encrypt that info in the database:\n\n```php\nuse R64\\Webforms\\Models\\FormStep;\n\n$personalInfoStep = FormStep::build($coffeeForm, 'Personal info')\n    -\u003eisPersonalData(1)\n    -\u003esave();\n```\n\nThen add the questions:\n\n```php\nuse R64\\Webforms\\Models\\Question;\nuse R64\\Webforms\\QuestionTypes\\IntegerType;\n\n$firstNameQuestion = Question::build($personalInfoStep, 'First Name')\n    -\u003esave();\n\n$lastNameQuestion = Question::build($personalInfoStep, 'Last Name')\n    -\u003esave();\n\n$ageQuestion = Question::build($personalInfoStep, 'Birth Year')\n    -\u003etype(IntegerType::TYPE)\n    -\u003esave();\n```\n\nOnce we have that, we can add the questions steps to users:\n\n```php\nUser::all()-\u003eeach-\u003eaddFormSteps([$coffeeStep, $personalInfoStep]);\n```\n\nIf we want to add all the formSteps to the users we can also use:\n\n```php\nUser::all()-\u003eeach-\u003eaddFormSteps();\n```\n\nWhen the users ask for the forms they will get only the forms they had steps on it. We need to do an authenticated request to:\n\n`/webforms/forms`\n\nWe'll get something like:\n\n```json\n{\n    \"data\": [\n        {\n            \"id\": 1,\n            \"sort\": 1,\n            \"slug\": \"coffee-form\",\n            \"menu_title\": null,\n            \"title\": \"Coffee Form\",\n            \"description\": null,\n            \"completed\": false\n        }\n    ]\n}\n```\n\nLet's say the form is the one with id 1. Then we can make another one to:\n\n`/webforms/form-steps?form=1`\n\nWe'll obtain all the forms steps info:\n\n```json\n{\n    \"data\": [\n        {\n            \"id\": 1,\n            \"form\": {\n                \"id\": 1,\n                \"sort\": 1,\n                \"slug\": \"coffee-form\",\n                \"menu_title\": \"\",\n                \"title\": \"Coffee Form\",\n                \"description\": \"\",\n                \"completed\": false\n            },\n            \"sort\": 1,\n            \"slug\": \"coffee\",\n            \"menu_title\": null,\n            \"title\": \"Coffee\",\n            \"description\": \"\",\n            \"completed\": false\n        },\n        {\n            \"id\": 2,\n            \"form\": {\n                \"id\": 1,\n                \"sort\": 1,\n                \"slug\": \"coffee-form\",\n                \"menu_title\": null,\n                \"title\": \"Coffee Form\",\n                \"description\": null,\n                \"completed\": false\n            },\n            \"sort\": 2,\n            \"slug\": \"personal-info\",\n            \"menu_title\": null,\n            \"title\": \"Personal info\",\n            \"description\": null,\n            \"completed\": false\n        }\n    ]\n}\n```\n\nFor each form step we need to ask for the questions using:\n\n`/webforms/questions?form_step=1`\n\n```json\n{\n    \"data\": [\n        {\n            \"id\": 1,\n            \"form_step\": {\n                \"id\": 1,\n                \"sort\": 1,\n                \"slug\": \"coffee\",\n                \"menu_title\": null,\n                \"title\": \"Coffee\",\n                \"description\": \"\",\n                \"completed\": false\n            },\n            \"sort\": 1,\n            \"depends_on\": null,\n            \"shown_when\": null,\n            \"required\": false,\n            \"slug\": \"what-kind-of-coffee-do-you-like\",\n            \"group_by\": null,\n            \"group_by_description\": null,\n            \"label_position\": \"left\",\n            \"help_title\": null,\n            \"help_body\": null,\n            \"type\": \"options\",\n            \"post_input_text\": null,\n            \"title\": \"What kind of coffee do you like?\",\n            \"description\": null,\n            \"error_message\": null,\n            \"default_value\": null,\n            \"min\": null,\n            \"max\": null,\n            \"options\": [\n                {\n                    \"label\": \"Black\",\n                    \"value\": \"black\"\n                },\n                {\n                    \"label\": \"Latte\",\n                    \"value\": \"latte\"\n                },\n                {\n                    \"label\": \"Cappucino\",\n                    \"value\": \"capuccino\"\n                },\n                {\n                    \"label\": \"Americano\",\n                    \"value\": \"americano\"\n                },\n                {\n                    \"label\": \"Red Eye\",\n                    \"value\": \"red-eye\"\n                },\n                {\n                    \"label\": \"Flat White\",\n                    \"value\": \"flat-white\"\n                }\n            ],\n            \"answer\": {}\n        },\n        {\n            \"id\": 2,\n            \"form_step\": {\n                \"id\": 1,\n                \"sort\": 1,\n                \"slug\": \"coffee\",\n                \"menu_title\": null,\n                \"title\": \"Coffee\",\n                \"description\": \"\",\n                \"completed\": false\n            },\n            \"sort\": 2,\n            \"depends_on\": null,\n            \"shown_when\": null,\n            \"required\": false,\n            \"slug\": \"what-type-of-coffee-beans-do-you-like-the-most\",\n            \"group_by\": null,\n            \"group_by_description\": null,\n            \"label_position\": \"left\",\n            \"help_title\": null,\n            \"help_body\": null,\n            \"type\": \"options\",\n            \"post_input_text\": null,\n            \"title\": \"What type of coffee beans do you like the most?\",\n            \"description\": null,\n            \"error_message\": null,\n            \"default_value\": null,\n            \"min\": null,\n            \"max\": null,\n            \"options\": [\n                {\n                    \"label\": \"Arabica\",\n                    \"value\": \"arabica\"\n                },\n                {\n                    \"label\": \"Robusta\",\n                    \"value\": \"robusta\"\n                }\n            ],\n            \"answer\": {}\n        }\n    ]\n}\n```\n\nWe need to do the same with the personal info step:\n\n`/webforms/questions?form_step=2`\n\n```json\n{\n    \"data\": [\n        {\n            \"id\": 3,\n            \"form_step\": {\n                \"id\": 2,\n                \"sort\": 2,\n                \"slug\": \"personal-info\",\n                \"menu_title\": null,\n                \"title\": \"Personal info\",\n                \"description\": \"\",\n                \"completed\": false\n            },\n            \"sort\": 3,\n            \"depends_on\": null,\n            \"shown_when\": null,\n            \"required\": false,\n            \"slug\": \"first-name\",\n            \"group_by\": null,\n            \"group_by_description\": null,\n            \"label_position\": \"left\",\n            \"help_title\": null,\n            \"help_body\": null,\n            \"type\": \"text\",\n            \"post_input_text\": null,\n            \"title\": \"First Name\",\n            \"description\": null,\n            \"error_message\": null,\n            \"default_value\": null,\n            \"min\": null,\n            \"max\": null,\n            \"options\": null,\n            \"answer\": {}\n        },\n        {\n            \"id\": 4,\n            \"form_step\": {\n                \"id\": 2,\n                \"sort\": 2,\n                \"slug\": \"personal-info\",\n                \"menu_title\": null,\n                \"title\": \"Personal info\",\n                \"description\": \"\",\n                \"completed\": false\n            },\n            \"sort\": 4,\n            \"depends_on\": null,\n            \"shown_when\": null,\n            \"required\": false,\n            \"slug\": \"last-name\",\n            \"group_by\": null,\n            \"group_by_description\": null,\n            \"label_position\": \"left\",\n            \"help_title\": null,\n            \"help_body\": null,\n            \"type\": \"text\",\n            \"post_input_text\": null,\n            \"title\": \"Last Name\",\n            \"description\": null,\n            \"error_message\": null,\n            \"default_value\": null,\n            \"min\": null,\n            \"max\": null,\n            \"options\": null,\n            \"answer\": {}\n        },\n        {\n            \"id\": 5,\n            \"form_step\": {\n                \"id\": 1,\n                \"sort\": 1,\n                \"slug\": \"coffee\",\n                \"menu_title\": null,\n                \"title\": \"Coffee\",\n                \"description\": \"\",\n                \"completed\": false\n            },\n            \"sort\": 4,\n            \"depends_on\": null,\n            \"shown_when\": null,\n            \"required\": false,\n            \"slug\": \"birth-year\",\n            \"group_by\": null,\n            \"group_by_description\": null,\n            \"label_position\": \"left\",\n            \"help_title\": null,\n            \"help_body\": null,\n            \"type\": \"integer\",\n            \"post_input_text\": null,\n            \"title\": \"Birth Year\",\n            \"description\": null,\n            \"error_message\": null,\n            \"default_value\": null,\n            \"min\": null,\n            \"max\": null,\n            \"options\": null,\n            \"answer\": {}\n        }\n    ]\n}\n```\n\nWhen a user needs to send an answer to a question, we will need to make a POST request to:\n\n`/webforms/answers`\n\nWith the following payload:\n\n```json\n{\n    \"question_id\": 2,\n    \"text\": \"arabica\"\n}\n```\n\nWe will receive the question but now with an answer on it:\n\n```json\n{\n    \"data\": {\n        \"id\": 2,\n        \"form_step\": {\n            \"id\": 1,\n            \"sort\": 1,\n            \"slug\": \"coffee\",\n            \"menu_title\": null,\n            \"title\": \"Coffee\",\n            \"description\": \"\",\n            \"completed\": false\n        },\n        \"sort\": 2,\n        \"depends_on\": null,\n        \"shown_when\": null,\n        \"required\": false,\n        \"slug\": \"what-type-of-coffee-beans-do-you-like-the-most\",\n        \"group_by\": null,\n        \"group_by_description\": null,\n        \"label_position\": \"left\",\n        \"help_title\": null,\n        \"help_body\": null,\n        \"type\": \"options\",\n        \"post_input_text\": null,\n        \"title\": \"What type of coffee beans do you like the most?\",\n        \"description\": null,\n        \"error_message\": null,\n        \"default_value\": null,\n        \"min\": null,\n        \"max\": null,\n        \"options\": [\n            {\n                \"label\": \"Arabica\",\n                \"value\": \"arabica\"\n            },\n            {\n                \"label\": \"Robusta\",\n                \"value\": \"robusta\"\n            }\n        ],\n        \"answer\": {\n            \"id\": 123,\n            \"user_id\": 10,\n            \"question_id\": 2,\n            \"text\": \"arabica\",\n            \"confirmed\": true\n        }\n    }\n}\n```\n\n## Testing\n\nCopy `phpunit.xml.dist` to `phpunit.xml`\n\n```bash\ncp phpunit.xml.dist phpunit.xml\n```\n\nAdapt or change the values in the next portion of code to your preferences:\n\n```xml\n    \u003cphp\u003e\n        \u003cenv name=\"DB_CONNECTION\" value=\"mysql\"/\u003e\n        \u003cenv name=\"DB_USERNAME\" value=\"root\"/\u003e\n        \u003cenv name=\"DB_PASSWORD\" value=\"\"/\u003e\n        \u003cenv name=\"DB_DATABASE\" value=\"r64_webforms\"/\u003e\n        \u003cenv name=\"DB_HOST\" value=\"127.0.0.1\"/\u003e\n        \u003cenv name=\"DB_PORT\" value=\"3306\"/\u003e\n    \u003c/php\u003e\n```\n\nCreate the database, in this case `r64_webforms`.\n\nExecute:\n\n``` bash\ncomposer test\n```\n\n## Changelog\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.\n\n## Contributing\n\nPlease see [CONTRIBUTING](CONTRIBUTING.md) for details.\n\n## Security\n\nIf you discover any security related issues, please email mmanzano@gmail.com instead of using the issue tracker.\n\n## Credits\n\n- [64 Robots](https://github.com/64Robots)\n- [All Contributors](../../contributors)\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n\n## Acknowledgments\n\nThanks to [Spatie](https://spatie.be/) for the [Package Skeleton Laravel](https://github.com/spatie/package-skeleton-laravel).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F64robots%2Fwebforms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F64robots%2Fwebforms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F64robots%2Fwebforms/lists"}