├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── LICENSE ├── README.md ├── SECURITY.md ├── composer.json ├── composer.lock └── src ├── Authorization ├── Authorization.php ├── AuthorizationBuilder.php └── Permission │ ├── Factory.php │ ├── Iterator.php │ ├── IteratorFactory.php │ └── Permission.php ├── Contracts ├── Authorization │ ├── Authorization.php │ └── Permission.php ├── Database │ ├── Database.php │ ├── Handler.php │ ├── Strategy.php │ └── _Relationship.php ├── Elements │ └── Element.php ├── Event │ └── Event.php ├── Support │ └── Arr.php ├── User │ └── User.php └── Validation │ ├── Strategy.php │ ├── Validation.php │ └── Validator.php ├── Controllers └── FormController.php ├── Database ├── Database.php ├── DatabaseBuilder.php ├── Handler.php ├── Strategies │ ├── CollectionStrategy.php │ ├── DefaultStrategy.php │ ├── GroupStrategy.php │ └── Strategy.php └── StrategyBuilder.php ├── Elements ├── CheckboxElement.php ├── CheckboxgroupElement.php ├── CollectionElement.php ├── Element.php ├── Factory.php ├── HiddenElement.php ├── KeyElement.php ├── MetaElement.php ├── MultiselectElement.php ├── PasswordElement.php ├── RadioElement.php ├── RadiogroupElement.php ├── SelectElement.php ├── StaticElement.php ├── TextElement.php └── TextareaElement.php ├── Event └── Event.php ├── Laraform.php ├── LaraformServiceProvider.php ├── Process └── AutoProcess.php ├── Support ├── Arr.php ├── Hash.php └── Json.php ├── Traits ├── ProcessesForm.php ├── StoresForm.php └── UpdatesForm.php ├── User ├── User.php └── UserBuilder.php ├── Validation ├── Strategies │ ├── DefaultStrategy.php │ ├── GroupStrategy.php │ └── Strategy.php ├── StrategyBuilder.php ├── Validation.php └── Validator.php ├── config └── laraform.php └── routes.php /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please do not create issues here, but procced to main Laraform repository: 2 | https://github.com/laraform/laraform/issues/new/choose -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .DS_Store 3 | vendor/ 4 | tests/ 5 | *.code-workspace -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License* 2 | 3 | Copyright (c) 2018-2019 Adam Berecz 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | * Valid for Laraform Community Edition. For full version please check: 24 | https://laraform.io/terms -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laraform Community Edition (Laravel) 2 | 3 | This repository contains the Laravel package of Laraform's Community Edition. 4 | 5 | Check out [laraform/laraform](https://github.com/laraform/laraform) repo or [Documentation](https://laraform.io/docs) for more details. 6 | 7 | ## Installation 8 | 9 | 10 | ``` bash 11 | composer require laraform/laraform-laravel 12 | ``` 13 | 14 | ## Usage 15 | 16 | ``` php 17 | // app/Forms/MyFirstForm.php 18 | 19 | [ 29 | 'type' => 'text', 30 | 'label' => 'Hello', 31 | 'default' => 'World' 32 | ] 33 | ]; 34 | } 35 | ``` 36 | 37 | Pass the form to the view: 38 | ``` php 39 | // routes/web.php 40 | 41 | Route::get('/', function () { 42 | return view('welcome', [ 43 | 'form' => app('App\Forms\MyFirstForm') 44 | ]); 45 | }); 46 | ``` 47 | 48 | Render: 49 | ``` html 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
60 | {!! $form->render() !!} 61 |
62 | 63 | 64 | 65 | 66 | ``` 67 | 68 | Please note that you need the [Larafrom Vue package](https://github.com/laraform/laraform) in order to make this work. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | **PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, [SEE BELOW](#reporting-a-vulnerability).** 4 | 5 | ## Reporting a Vulnerability 6 | 7 | If you discover a security vulnerability within Laraform, please send an email to us at hello@laraform.io. All security vulnerabilities will be promptly addressed. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laraform/laraform-laravel", 3 | "description": "Laraform Community Edition (Laravel)", 4 | "version": "1.2.2", 5 | "type": "library", 6 | "keywords": [ 7 | "laravel", 8 | "form", 9 | "builder", 10 | "generator" 11 | ], 12 | "homepage": "https://laraform.io", 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "Adam Berecz", 17 | "email": "adam@laraform.io" 18 | } 19 | ], 20 | "support": { 21 | "issues": "https://github.com/laraform/laraform/issues", 22 | "docs": "https://laraform.io/docs" 23 | }, 24 | "require": { 25 | "php": ">=5.5.9", 26 | "hashids/hashids": "^3.0 || ^4.0" 27 | }, 28 | "require-dev": { 29 | "orchestra/testbench": "~3.0", 30 | "phpunit/phpunit": "^7.5" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Laraform\\": "src/" 35 | } 36 | }, 37 | "extra": { 38 | "laravel": { 39 | "providers": [ 40 | "Laraform\\LaraformServiceProvider" 41 | ], 42 | "aliases": { 43 | "Laraform": "Laraform\\Laraform" 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/Authorization/Authorization.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 35 | 36 | foreach ($this->permittable as $action) { 37 | $this->permissions[$action] = $this->makePermissions($action, $builder->getPermissions()); 38 | } 39 | } 40 | 41 | /** 42 | * Determine if a user is allowed to perform an action 43 | * 44 | * @param string $action 45 | * @param User $user 46 | * @param object $entity 47 | * @return void 48 | */ 49 | public function authorize($action, User $user, $entity = null) 50 | { 51 | if (!in_array($action, $this->permittable)) { 52 | throw new \InvalidArgumentException("Unknown action: $action"); 53 | } 54 | 55 | if (!$this->hasPermissions()) { 56 | return true; 57 | } 58 | 59 | return $this->can($action, $user, $entity); 60 | } 61 | 62 | /** 63 | * Permit the user to perform action 64 | * 65 | * @param string $action 66 | * @param string $role 67 | * @param callable $callback 68 | * @return void 69 | */ 70 | public function permit($action, $role = null, callable $callback = null) 71 | { 72 | $this->permissions[$action]->add($role, $callback); 73 | } 74 | 75 | /** 76 | * Determine if a user can perform an action 77 | * 78 | * @param string $action 79 | * @param User $user 80 | * @param object $entity 81 | * @return boolean 82 | */ 83 | private function can($action, User $user, $entity = null) 84 | { 85 | while ($this->permissions[$action]->valid()) { 86 | if ($this->permissions[$action]->current()->allowed($user, $entity)) { 87 | return true; 88 | } 89 | 90 | $this->permissions[$action]->next(); 91 | } 92 | 93 | return false; 94 | } 95 | 96 | /** 97 | * Determine if a user can't perform an action 98 | * 99 | * @param string $action 100 | * @param User $user 101 | * @param object $entity 102 | * @return boolean 103 | */ 104 | private function cant($action, User $user, $entity = null) 105 | { 106 | return !$this->can($action, $user, $entity); 107 | } 108 | 109 | /** 110 | * Determine if there are any permissins set 111 | * 112 | * @return boolean 113 | */ 114 | private function hasPermissions() 115 | { 116 | foreach ($this->permittable as $permittable) { 117 | if ($this->permissions[$permittable]->count() > 0) { 118 | return true; 119 | } 120 | } 121 | 122 | return false; 123 | } 124 | 125 | /** 126 | * Create new Iterator instance 127 | * 128 | * @param string $action 129 | * @param array $permissions 130 | * @return Laraform\Permission\Iterator; 131 | */ 132 | private function makePermissions($action, $permissions) 133 | { 134 | return $this->factory->make($this->createRoles($action, $permissions)); 135 | } 136 | 137 | /** 138 | * Create roles array 139 | * 140 | * @param string $action 141 | * @param array $permissions 142 | * @return array 143 | */ 144 | private function createRoles($action, $permissions) 145 | { 146 | if (in_array($action, $permissions)) { 147 | return ['*']; 148 | } elseif (array_key_exists($action, $permissions)) { 149 | return $permissions[$action]; 150 | } 151 | 152 | return []; 153 | } 154 | } -------------------------------------------------------------------------------- /src/Authorization/AuthorizationBuilder.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 36 | } 37 | 38 | /** 39 | * Add role to permission 40 | * 41 | * @param string $role 42 | * @param callable $callback 43 | * @return void 44 | */ 45 | public function add($role, callable $callback = null) 46 | { 47 | $this->permissions[] = $this->factory->make($role, $callback); 48 | } 49 | 50 | /** 51 | * Increment current key 52 | * 53 | * @return void 54 | */ 55 | public function next() 56 | { 57 | $this->current++; 58 | } 59 | 60 | /** 61 | * Determine if current key exists 62 | * 63 | * @return void 64 | */ 65 | public function valid() 66 | { 67 | return array_key_exists($this->current, $this->permissions); 68 | } 69 | 70 | /** 71 | * Return current item 72 | * 73 | * @return void 74 | */ 75 | public function current() 76 | { 77 | return $this->permissions[$this->current]; 78 | } 79 | 80 | /** 81 | * Return total number of items 82 | * 83 | * @return void 84 | */ 85 | public function count() 86 | { 87 | return count($this->permissions); 88 | } 89 | } -------------------------------------------------------------------------------- /src/Authorization/Permission/IteratorFactory.php: -------------------------------------------------------------------------------- 1 | add($role); 19 | } 20 | 21 | return $permissions; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Authorization/Permission/Permission.php: -------------------------------------------------------------------------------- 1 | role = $this->parseRole($role); 49 | $this->userAttribute = $this->parseUserAttribute($role); 50 | $this->callback = $callback; 51 | } 52 | 53 | /** 54 | * Determine if user is allowed to perform an action 55 | * 56 | * @param User $user 57 | * @param object $entity 58 | * @return boolean 59 | */ 60 | public function allowed(User $user, $entity = null) 61 | { 62 | if (!$this->isRoleAllowed($user) || !$this->isUserAllowed($user, $entity)) { 63 | return false; 64 | } 65 | 66 | return $this->hasCallback() 67 | ? $this->callback($user, $entity) 68 | : true; 69 | } 70 | 71 | /** 72 | * Determine if user's role is allowed to perform an action 73 | * 74 | * @param User $user 75 | * @return boolean 76 | */ 77 | private function isRoleAllowed(User $user) 78 | { 79 | if ($this->role !== null 80 | && $this->role !== self::WILDCARD 81 | && !in_array($this->role, $user->roles()) 82 | ) { 83 | return false; 84 | } 85 | 86 | return true; 87 | } 88 | 89 | /** 90 | * Determine if user is allowed to perform an action 91 | * 92 | * @param User $user 93 | * @param object $entity 94 | * @return boolean 95 | */ 96 | private function isUserAllowed(User $user, $entity = null) 97 | { 98 | if ($this->userAttribute === null) { 99 | return true; 100 | } 101 | 102 | if (!$user->authenticated() || !$this->isOwner($user, $entity)) { 103 | return false; 104 | } 105 | 106 | return true; 107 | } 108 | 109 | /** 110 | * Determine if user is owner of entity 111 | * 112 | * @param User $user 113 | * @param object $entity 114 | * @return boolean 115 | */ 116 | private function isOwner(User $user, $entity) 117 | { 118 | return $entity->{$this->userAttribute} == $user->key(); 119 | } 120 | 121 | /** 122 | * Determine if has callback 123 | * 124 | * @return boolean 125 | */ 126 | private function hasCallback() 127 | { 128 | return $this->callback !== null; 129 | } 130 | 131 | /** 132 | * Call the callback 133 | * 134 | * @param User $user 135 | * @param object $entity 136 | * @return boolean 137 | */ 138 | private function callback(User $user, $entity) 139 | { 140 | return call_user_func_array($this->callback, [ 141 | $user, $entity 142 | ]); 143 | } 144 | 145 | /** 146 | * Parse role 147 | * 148 | * @param string $role 149 | * @return void 150 | */ 151 | private function parseRole($role) 152 | { 153 | return $role !== null ? explode(':', $role)[0] : null; 154 | } 155 | 156 | /** 157 | * Parse user attribute 158 | * 159 | * @param string $role 160 | * @return void 161 | */ 162 | private function parseUserAttribute($role) 163 | { 164 | $parts = explode(':', $role); 165 | 166 | return isset($parts[1]) ? $parts[1] : null; 167 | } 168 | } -------------------------------------------------------------------------------- /src/Contracts/Authorization/Authorization.php: -------------------------------------------------------------------------------- 1 | database->update($target, $data); 74 | // } 75 | 76 | /** 77 | * Empty element's value on target 78 | * 79 | * @param object $target 80 | * @return void 81 | */ 82 | public function empty($target); 83 | 84 | /** 85 | * Save freshly inserted keys 86 | * 87 | * @param object $target 88 | * @return array 89 | */ 90 | public function getNewKeys($target); 91 | 92 | /** 93 | * Validate element 94 | * 95 | * @param Validator $validator 96 | * @param string $prefix 97 | * @return void 98 | */ 99 | public function validate(Validator $validator, $prefix = null); 100 | 101 | /** 102 | * Determines if the element should be validated 103 | * 104 | * @return boolean 105 | */ 106 | public function shouldValidate(); 107 | 108 | /** 109 | * Get schema array 110 | * 111 | * @param string $side 112 | * @return array 113 | */ 114 | public function getSchema($side); 115 | 116 | /** 117 | * Return languages 118 | * 119 | * @return array 120 | */ 121 | public function getLanguages(); 122 | 123 | /** 124 | * Return rules for side 125 | * 126 | * @param string $side 127 | * @return void 128 | */ 129 | public function getRules($side = 'backend'); 130 | 131 | /** 132 | * Determine if element has rules 133 | * 134 | * @param string $side 135 | * @return boolean 136 | */ 137 | public function hasRules($side = 'backend'); 138 | 139 | /** 140 | * Return custom message for rule 141 | * 142 | * @param string $rule 143 | * @return void 144 | */ 145 | public function getMessage($rule); 146 | 147 | /** 148 | * Determine if the element is presented in data 149 | * 150 | * @param array $data 151 | * @return boolean 152 | */ 153 | public function presentedIn(array $data); 154 | } -------------------------------------------------------------------------------- /src/Contracts/Event/Event.php: -------------------------------------------------------------------------------- 1 | process($request); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Database/Database.php: -------------------------------------------------------------------------------- 1 | form = $builder->getForm(); 31 | $this->handler = $this->makeHandler(); 32 | } 33 | 34 | /** 35 | * Find entity by key 36 | * 37 | * @param integer $key 38 | * @return object 39 | */ 40 | public function find($key) 41 | { 42 | return $this->getHandler()->find($key); 43 | } 44 | 45 | /** 46 | * Load data by key 47 | * 48 | * @param integer $key 49 | * @return array 50 | */ 51 | public function load($key) 52 | { 53 | return $this->getHandler()->load($key); 54 | } 55 | 56 | /** 57 | * Insert data 58 | * 59 | * @param array $data 60 | * @return void 61 | */ 62 | public function insert(array $data) 63 | { 64 | return $this->getHandler()->insert($data); 65 | } 66 | 67 | /** 68 | * Update entity with data 69 | * 70 | * @param array $data 71 | * @param integer $key 72 | * @param bool $emptyOnNull 73 | * @return void 74 | */ 75 | public function update(array $data, $key, $emptyOnNull = true) 76 | { 77 | return $this->getHandler()->update($data, $key, $emptyOnNull); 78 | } 79 | 80 | /** 81 | * Return freshly inserted keys 82 | * 83 | * @return array 84 | */ 85 | public function getNewKeys() 86 | { 87 | return $this->getHandler()->getNewKeys(); 88 | } 89 | 90 | /** 91 | * Return database entity 92 | * 93 | * @return mixed 94 | */ 95 | public function getEntity() 96 | { 97 | return $this->getHandler()->getEntity(); 98 | } 99 | 100 | /** 101 | * Return current Handler instance 102 | * 103 | * @return Handler 104 | */ 105 | protected function getHandler() 106 | { 107 | $this->setElements(); 108 | 109 | return $this->handler; 110 | } 111 | 112 | /** 113 | * Set elements for Handler 114 | * 115 | * @return void 116 | */ 117 | protected function setElements() 118 | { 119 | $this->handler->setElements($this->form->getElements()); 120 | } 121 | 122 | /** 123 | * Create new Database Handler instance 124 | * 125 | * @return Laraform\Contracts\Database\Handler 126 | */ 127 | protected function makeHandler() 128 | { 129 | return app()->makeWith($this->handler, [ 130 | 'model' => $this->form->model, 131 | ]); 132 | } 133 | } -------------------------------------------------------------------------------- /src/Database/DatabaseBuilder.php: -------------------------------------------------------------------------------- 1 | makeWith(Database::class, [ 22 | 'builder' => $this 23 | ]); 24 | } 25 | 26 | /** 27 | * Get the value of form 28 | */ 29 | public function getForm() 30 | { 31 | return $this->form; 32 | } 33 | 34 | /** 35 | * Set the value of form 36 | * 37 | * @return self 38 | */ 39 | public function setForm($form) 40 | { 41 | $this->form = $form; 42 | 43 | return $this; 44 | } 45 | } -------------------------------------------------------------------------------- /src/Database/Handler.php: -------------------------------------------------------------------------------- 1 | model = $model; 49 | $this->db = $db; 50 | } 51 | 52 | /** 53 | * Find an entity by key 54 | * 55 | * @param integer $key 56 | * @return Illuminate\Database\Eloquent\Model 57 | */ 58 | public function find($key) 59 | { 60 | return $this->entity = $this->model()->find($key); 61 | } 62 | 63 | /** 64 | * Return data of an entity 65 | * 66 | * @param integer $key 67 | * @return array 68 | */ 69 | public function load($key) 70 | { 71 | $this->find($key); 72 | 73 | return $this->retrive(); 74 | } 75 | 76 | /** 77 | * Insert data 78 | * 79 | * @param array $data 80 | * @return void 81 | */ 82 | public function insert(array $data) 83 | { 84 | $this->db::transaction(function() use ($data) { 85 | $this->create(); 86 | $this->fill($data); 87 | $this->save(); 88 | }); 89 | } 90 | 91 | /** 92 | * Update data 93 | * 94 | * @param array $data 95 | * @param integer $key 96 | * @param boolean $emptyOnNull 97 | * @return void 98 | */ 99 | public function update(array $data, $key, $emptyOnNull = true) 100 | { 101 | $this->db::transaction(function () use ($data, $key, $emptyOnNull) { 102 | $this->find($key); 103 | $this->fill($data, $emptyOnNull); 104 | $this->save(); 105 | }); 106 | } 107 | 108 | /** 109 | * Get freshly inserted keys 110 | * 111 | * @return array 112 | */ 113 | public function getNewKeys() 114 | { 115 | return $this->elements->getNewKeys($this->entity); 116 | } 117 | 118 | /** 119 | * Set value of elements 120 | * 121 | * @param Element $elements 122 | * @return void 123 | */ 124 | public function setElements(Element $elements) 125 | { 126 | $this->elements = $elements; 127 | } 128 | 129 | /** 130 | * Returns current entity 131 | * 132 | * @var Illuminate\Database\Eloquent\Model 133 | */ 134 | public function getEntity() 135 | { 136 | return $this->entity; 137 | } 138 | 139 | /** 140 | * Create and set new entity 141 | * 142 | * @return void 143 | */ 144 | private function create() 145 | { 146 | $this->entity = $this->model(); 147 | } 148 | 149 | /** 150 | * Save entity 151 | * 152 | * @return void 153 | */ 154 | private function save() 155 | { 156 | $this->entity->save(); 157 | } 158 | 159 | /** 160 | * Retrive data from elements 161 | * 162 | * @return void 163 | */ 164 | private function retrive() 165 | { 166 | return Arr::forceArray($this->elements->load($this->entity)); 167 | } 168 | 169 | /** 170 | * Fill elements with data 171 | * 172 | * @param array $data 173 | * @param boolean $emptyOnNull 174 | * @return void 175 | */ 176 | private function fill($data, $emptyOnNull = true) 177 | { 178 | $this->elements->fill($this->entity, $data, $emptyOnNull); 179 | } 180 | 181 | /** 182 | * Return model instance 183 | * 184 | * @return void 185 | */ 186 | private function model() 187 | { 188 | return app($this->model); 189 | } 190 | } -------------------------------------------------------------------------------- /src/Database/Strategies/CollectionStrategy.php: -------------------------------------------------------------------------------- 1 | children() as $child) { 20 | $data = array_merge($data, $child->load($target)); 21 | } 22 | 23 | return $data; 24 | } 25 | 26 | /** 27 | * Fill value to target from data 28 | * 29 | * @param Illuminate\Database\Eloquent\Model $target 30 | * @param array $data 31 | * @param boolean $emptyOnNull 32 | * @return void 33 | */ 34 | public function fill($target, array $data, $emptyOnNull = true) 35 | { 36 | foreach ($this->children() as $child) { 37 | $child->fill($target, $data, $emptyOnNull); 38 | } 39 | } 40 | 41 | /** 42 | * Return freshly inserted keys 43 | * 44 | * @param Illuminate\Database\Eloquent\Model $target 45 | * @return array 46 | */ 47 | public function getNewKeys($target) 48 | { 49 | $keys = []; 50 | foreach ($this->children() as $child) { 51 | $childKeys = $child->getNewKeys($target); 52 | 53 | if (!empty($childKeys)) { 54 | $keys[$child->attribute] = $childKeys; 55 | } 56 | } 57 | 58 | return $keys; 59 | } 60 | } -------------------------------------------------------------------------------- /src/Database/Strategies/DefaultStrategy.php: -------------------------------------------------------------------------------- 1 | name() => $target[$this->attribute()] 17 | ]; 18 | } 19 | 20 | /** 21 | * Fill value to target from data 22 | * 23 | * @param Illuminate\Database\Eloquent\Model $target 24 | * @param array $data 25 | * @param boolean $emptyOnNull 26 | * @return void 27 | */ 28 | public function fill($target, array $data, $emptyOnNull = true) 29 | { 30 | $target[$this->attribute()] = $data[$this->name()]; 31 | } 32 | 33 | /** 34 | * Empty value on target 35 | * 36 | * @param object $target 37 | * @return void 38 | */ 39 | public function empty($target) 40 | { 41 | $target[$this->attribute()] = null; 42 | } 43 | 44 | /** 45 | * Return freshly inserted keys 46 | * 47 | * @param Illuminate\Database\Eloquent\Model $target 48 | * @return array 49 | */ 50 | public function getNewKeys($target) 51 | { 52 | if ($this->attribute() == $target->getKeyName() && $target->wasRecentlyCreated) { 53 | return $target[$this->attribute()]; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/Database/Strategies/GroupStrategy.php: -------------------------------------------------------------------------------- 1 | children() as $child) { 20 | $data = array_merge($data, $child->load($target)); 21 | } 22 | 23 | return $data; 24 | } 25 | } -------------------------------------------------------------------------------- /src/Database/Strategies/Strategy.php: -------------------------------------------------------------------------------- 1 | setElement($builder->getElement()); 33 | } 34 | 35 | /** 36 | * Load value to target 37 | * 38 | * @param object $target 39 | * @return void 40 | */ 41 | public function load($target) 42 | { 43 | throw new \BadMethodCallException('Unimplemented method'); 44 | } 45 | 46 | /** 47 | * Fill value to target from data 48 | * 49 | * @param object $target 50 | * @param array $data 51 | * @param boolean $emptyOnNull 52 | * @return void 53 | */ 54 | public function fill($target, array $data, $emptyOnNull = true) 55 | { 56 | throw new \BadMethodCallException('Unimplemented method'); 57 | } 58 | 59 | /** 60 | * Empty value on target 61 | * 62 | * @param object $target 63 | * @return void 64 | */ 65 | public function empty($target) 66 | { 67 | throw new \BadMethodCallException('Unimplemented method'); 68 | } 69 | 70 | /** 71 | * Return freshly inserted keys 72 | * 73 | * @param object $target 74 | * @return array 75 | */ 76 | public function getNewKeys($target) 77 | { 78 | throw new \BadMethodCallException('Unimplemented method'); 79 | } 80 | 81 | /** 82 | * Determine if this has entity 83 | * 84 | * @return boolean 85 | */ 86 | protected function hasEntity() 87 | { 88 | return $this->entity !== null; 89 | } 90 | 91 | /** 92 | * Set value for Element 93 | * 94 | * @param Element $element 95 | * @return void 96 | */ 97 | protected function setElement(Element $element) 98 | { 99 | $this->element = $element; 100 | } 101 | 102 | /** 103 | * Return children of element 104 | * 105 | * @return Laraform\Contracts\Elements\Element[] 106 | */ 107 | protected function children() 108 | { 109 | return $this->element->children; 110 | } 111 | 112 | /** 113 | * Return value of element from entity 114 | * 115 | * @return mixed 116 | */ 117 | protected function value() 118 | { 119 | return $this->entity[$this->attribute()]; 120 | } 121 | 122 | /** 123 | * Return attribute of element 124 | * 125 | * @return string 126 | */ 127 | protected function attribute() 128 | { 129 | return $this->element->attribute; 130 | } 131 | 132 | /** 133 | * Return name of element 134 | * 135 | * @return string 136 | */ 137 | protected function name() 138 | { 139 | return $this->element->name; 140 | } 141 | } -------------------------------------------------------------------------------- /src/Database/StrategyBuilder.php: -------------------------------------------------------------------------------- 1 | \Laraform\Database\Strategies\DefaultStrategy::class, 23 | 'group' => \Laraform\Database\Strategies\GroupStrategy::class, 24 | ]; 25 | 26 | /** 27 | * Return new Strategy 28 | * 29 | * @return Laraform\Contracts\Database\Strategy 30 | */ 31 | public function build() 32 | { 33 | $strategy = array_key_exists($this->element->behaveAs, $this->strategies) 34 | ? $this->strategies[$this->element->behaveAs] 35 | : $this->strategies['default']; 36 | 37 | return app()->makeWith($strategy, [ 38 | 'builder' => $this 39 | ]); 40 | } 41 | 42 | /** 43 | * Get the value of element 44 | */ 45 | public function getElement() 46 | { 47 | return $this->element; 48 | } 49 | 50 | /** 51 | * Set the value of element 52 | * 53 | * @return self 54 | */ 55 | public function setElement(Element $element) 56 | { 57 | $this->element = $element; 58 | 59 | return $this; 60 | } 61 | } -------------------------------------------------------------------------------- /src/Elements/CheckboxElement.php: -------------------------------------------------------------------------------- 1 | setChildren(); 32 | } 33 | 34 | /** 35 | * Set Element level data from data array 36 | * 37 | * @param array $data 38 | * @return void 39 | */ 40 | public function setData($data) 41 | { 42 | $this->data = $data; 43 | 44 | foreach ($this->children as $child) { 45 | $child->setData($this->data); 46 | } 47 | } 48 | 49 | /** 50 | * Get Element's data with it's key 51 | * 52 | * @return array 53 | */ 54 | public function getData() 55 | { 56 | $data = []; 57 | 58 | foreach ($this->children as $child) { 59 | $data = array_merge($data, $child->getData()); 60 | } 61 | 62 | return $data; 63 | } 64 | 65 | /** 66 | * Convert Element's data to validation format 67 | * 68 | * @return mixed 69 | */ 70 | public function getValidationData() 71 | { 72 | $data = []; 73 | 74 | foreach ($this->children as $child) { 75 | $data = array_merge($data, $child->getValidationData()); 76 | } 77 | 78 | return $data; 79 | } 80 | 81 | /** 82 | * Determine if element has rules 83 | * 84 | * @param string $side 85 | * @return boolean 86 | */ 87 | public function hasRules($side = 'backend') 88 | { 89 | foreach ($this->children as $child) { 90 | if ($child->hasRules($side)) { 91 | return true; 92 | } 93 | } 94 | 95 | return false; 96 | } 97 | 98 | /** 99 | * Validate element 100 | * 101 | * @param Validator $validator 102 | * @param string $prefix 103 | * @return void 104 | */ 105 | public function validate(Validator $validator, $prefix = null) 106 | { 107 | return $this->validator->validate($validator, $prefix); 108 | } 109 | 110 | /** 111 | * Get schema array 112 | * 113 | * @param string $side 114 | * @return array 115 | */ 116 | public function getSchema($side) 117 | { 118 | $schema = $this->schema; 119 | 120 | foreach ($this->children as $child) { 121 | $schema[$child->name] = $child->getSchema($side); 122 | } 123 | 124 | return $schema; 125 | } 126 | 127 | /** 128 | * Determine if the element is presented in data 129 | * 130 | * @param array $data 131 | * @return boolean 132 | */ 133 | public function presentedIn(array $data) 134 | { 135 | return true; 136 | } 137 | 138 | /** 139 | * Return child by key 140 | * 141 | * @param string $key 142 | * @return Element 143 | */ 144 | public function getChildByKey($key) 145 | { 146 | return $this->children[$key]; 147 | } 148 | 149 | /** 150 | * Determine if has child with key 151 | * 152 | * @param string $key 153 | * @return boolean 154 | */ 155 | public function hasChildWithKey($key) 156 | { 157 | return array_key_exists($key, $this->children); 158 | } 159 | 160 | /** 161 | * Set children based on schema 162 | * 163 | * @return void 164 | */ 165 | protected function setChildren() 166 | { 167 | foreach ($this->schema as $name => $schema) { 168 | $this->addChild($this->makeChild($schema, $name)); 169 | } 170 | } 171 | 172 | /** 173 | * Make new Element instance 174 | * 175 | * @param array $schema 176 | * @param string $name 177 | * @return Element 178 | */ 179 | protected function makeChild($schema, $name) 180 | { 181 | return $this->factory->make($schema, $name, $this->options); 182 | } 183 | 184 | /** 185 | * Add child to children 186 | * 187 | * @param Element $child 188 | * @return void 189 | */ 190 | protected function addChild(Element $child) 191 | { 192 | $this->children[$child->name] = $child; 193 | } 194 | 195 | /** 196 | * Initalize class properties 197 | * 198 | * @return void 199 | */ 200 | protected function initProperties() 201 | { 202 | 203 | } 204 | } -------------------------------------------------------------------------------- /src/Elements/Element.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 125 | 126 | $this->schema = $schema; 127 | 128 | $this->options = array_merge($this->options, $options); 129 | 130 | $this->initProperties(); 131 | 132 | $this->database = $databaseBuilder 133 | ->setElement($this) 134 | ->build(); 135 | 136 | $this->validator = $validatorBuilder 137 | ->setElement($this) 138 | ->build(); 139 | } 140 | 141 | /** 142 | * Returns the Vue component's name to render 143 | * 144 | * @return string 145 | */ 146 | public function getComponent() 147 | { 148 | if (isset($this->schema['component'])) { 149 | return $this->schema['component']; 150 | } 151 | 152 | if ($this->component) { 153 | return $this->component; 154 | } 155 | 156 | return $this->getType() . '-element'; 157 | } 158 | 159 | /** 160 | * Returns element type 161 | * 162 | * @return string 163 | */ 164 | public function getType() 165 | { 166 | return $this->schema['type']; 167 | } 168 | 169 | /** 170 | * Set Element level data from data array 171 | * 172 | * @param array $data 173 | * @return void 174 | */ 175 | public function setData($data) 176 | { 177 | $this->data = $data; 178 | } 179 | 180 | /** 181 | * Get Element's data with it's key 182 | * 183 | * @return array 184 | */ 185 | public function getData() 186 | { 187 | return [ 188 | $this->name => $this->data[$this->name] 189 | ]; 190 | } 191 | 192 | /** 193 | * Get Element's value 194 | * 195 | * @return array 196 | */ 197 | public function value() 198 | { 199 | return $this->data[$this->name]; 200 | } 201 | 202 | /** 203 | * Updates the field's data in data property 204 | * 205 | * @param mixed $value 206 | * @return void 207 | */ 208 | public function updateValue($value) 209 | { 210 | $this->data[$this->name] = $value; 211 | } 212 | 213 | /** 214 | * Convert Element's data to validation format 215 | * 216 | * @return mixed 217 | */ 218 | public function getValidationData() 219 | { 220 | // Being commented because dependent fields like 221 | // _confirmation will not provide a data for the 222 | // validator which breaks the validation 223 | // if (!$this->shouldValidate()) { 224 | // return []; 225 | // } 226 | 227 | if (!$this->presentedIn($this->data)) { 228 | return []; 229 | } 230 | 231 | 232 | return [ 233 | $this->name => $this->data[$this->name] 234 | ]; 235 | } 236 | 237 | /** 238 | * Load element data from target 239 | * 240 | * @param object $target 241 | * @return array 242 | */ 243 | public function load($target) 244 | { 245 | if ($target === null || !$this->shouldPersist()) { 246 | return []; 247 | } 248 | 249 | return $this->database->load($target); 250 | } 251 | 252 | /** 253 | * Fill element data to target 254 | * 255 | * @param object $target 256 | * @param array $data 257 | * @param boolean $emptyOnNull 258 | * @return void 259 | */ 260 | public function fill($target, $data, $emptyOnNull = true) 261 | { 262 | if (!$this->shouldPersist()) { 263 | return; 264 | } 265 | 266 | $this->presentedIn($data) 267 | ? $this->database->fill($target, $data, $emptyOnNull) 268 | : ($emptyOnNull ? $this->empty($target) : ''); 269 | } 270 | 271 | // public function update($target, $data) 272 | // { 273 | // $this->database->update($target, $data); 274 | // } 275 | 276 | /** 277 | * Empty element's value on target 278 | * 279 | * @param object $target 280 | * @return void 281 | */ 282 | public function empty($target) 283 | { 284 | if (!$this->shouldPersist()) { 285 | return; 286 | } 287 | 288 | $this->database->empty($target); 289 | } 290 | 291 | /** 292 | * Save freshly inserted keys 293 | * 294 | * @param object $target 295 | * @return array 296 | */ 297 | public function getNewKeys($target) 298 | { 299 | if ($target === null || !$this->shouldPersist()) { 300 | return; 301 | } 302 | 303 | return $this->database->getNewKeys($target); 304 | } 305 | 306 | /** 307 | * Validate element 308 | * 309 | * @param Validator $validator 310 | * @param string $prefix 311 | * @return void 312 | */ 313 | public function validate(Validator $validator, $prefix = null) 314 | { 315 | if (!$this->shouldValidate()) { 316 | return; 317 | } 318 | 319 | $this->validator->validate($validator, $prefix); 320 | } 321 | 322 | /** 323 | * Determines if the element should be validated 324 | * 325 | * @return boolean 326 | */ 327 | public function shouldValidate() 328 | { 329 | return $this->presentedIn($this->data) && $this->hasRules(); 330 | } 331 | 332 | /** 333 | * Determines if the element should be persisted 334 | * 335 | * @return boolean 336 | */ 337 | public function shouldPersist() 338 | { 339 | return $this->persist; 340 | } 341 | 342 | /** 343 | * Get schema array 344 | * 345 | * @param string $side 346 | * @return array 347 | */ 348 | public function getSchema($side) 349 | { 350 | $schema = $this->schema; 351 | 352 | $schema = $this->correctRules($side, $schema); 353 | 354 | $schema['component'] = $this->getComponent(); 355 | 356 | unset($schema['persist']); 357 | 358 | return $schema; 359 | } 360 | 361 | /** 362 | * Remove unnecessary rules 363 | * 364 | * @param string $side 365 | * @param array $schema 366 | * @return array 367 | */ 368 | protected function correctRules($side, $schema) 369 | { 370 | if ($this->hasRules($side)) { 371 | $schema['rules'] = $this->getRules($side); 372 | } elseif (array_key_exists('rules', $schema)) { 373 | unset($schema['rules']); 374 | } 375 | 376 | return $schema; 377 | } 378 | 379 | /** 380 | * Return languages 381 | * 382 | * @return array 383 | */ 384 | public function getLanguages() 385 | { 386 | return $this->options['languages']; 387 | } 388 | 389 | /** 390 | * Return rules for side 391 | * 392 | * @param string $side 393 | * @return void 394 | */ 395 | public function getRules($side = 'backend') 396 | { 397 | if (!$this->hasRules($side)) { 398 | return; 399 | } 400 | 401 | $rules = $this->rules; 402 | 403 | if ($this->rulesHas($side)) { 404 | $rules = $rules[$side]; 405 | } 406 | 407 | return $rules; 408 | } 409 | 410 | /** 411 | * Determine if element has rules 412 | * 413 | * @param string $side 414 | * @return boolean 415 | */ 416 | public function hasRules($side = 'backend') 417 | { 418 | if (is_array($this->rules)) { 419 | if (array_key_exists($side, $this->rules)) { 420 | return true; 421 | } 422 | 423 | if (array_key_exists('frontend', $this->rules) || array_key_exists('backend', $this->rules)) { 424 | return false; 425 | } 426 | } 427 | 428 | return $this->rules !== null; 429 | } 430 | 431 | /** 432 | * Return custom message for rule 433 | * 434 | * @param string|object $rule 435 | * @return void 436 | */ 437 | public function getMessage($rule) 438 | { 439 | $ruleName = $rule; 440 | 441 | if (!is_string($rule)) { 442 | if ($rule instanceof \Illuminate\Validation\Rules\Unique) { 443 | $ruleName = 'unique'; 444 | } 445 | elseif ($rule instanceof \Illuminate\Validation\Rules\Exists) { 446 | $ruleName = 'exists'; 447 | } 448 | } 449 | 450 | if (array_key_exists($ruleName, $this->messages)) { 451 | return $this->messages[$ruleName]; 452 | } 453 | } 454 | 455 | /** 456 | * Determine if the element is presented in data 457 | * 458 | * @param array $data 459 | * @return boolean 460 | */ 461 | public function presentedIn(array $data) 462 | { 463 | return array_key_exists($this->name, $data); 464 | } 465 | 466 | /** 467 | * Store related files and returns it's filenames 468 | * 469 | * @param mixed $entity 470 | * @return array 471 | */ 472 | public function storeFiles($entity) 473 | { 474 | return []; 475 | } 476 | 477 | /** 478 | * Returns all files on entity 479 | * 480 | * @param mixed $entity 481 | * @return array 482 | */ 483 | public function originalFiles($entity) 484 | { 485 | return []; 486 | } 487 | 488 | /** 489 | * Returns all files based on current data 490 | * 491 | * @return array 492 | */ 493 | public function currentFiles() 494 | { 495 | return []; 496 | } 497 | 498 | /** 499 | * Determine if rules has certain key 500 | * 501 | * @param string $key 502 | * @param mixed $rules 503 | * @return void 504 | */ 505 | protected function rulesHas($key, $rules = null) 506 | { 507 | if ($rules === null) { 508 | $rules = $this->rules; 509 | } 510 | 511 | return is_array($rules) && array_key_exists($key, $rules); 512 | } 513 | 514 | /** 515 | * Initalize class properties 516 | * 517 | * @return void 518 | */ 519 | protected function initProperties() 520 | { 521 | $this->name = $this->schema['name'] ?? $this->name; 522 | $this->rules = $this->schema['rules'] ?? $this->rules; 523 | $this->messages = $this->schema['messages'] ?? $this->messages; 524 | $this->persist = $this->schema['persist'] ?? $this->persist; 525 | $this->attribute = $this->schema['attribute'] ?? $this->name; 526 | } 527 | } -------------------------------------------------------------------------------- /src/Elements/Factory.php: -------------------------------------------------------------------------------- 1 | makeWith($this->getClass($type), compact('schema', 'options')); 24 | } 25 | 26 | /** 27 | * Returns element class 28 | * 29 | * @param string $type 30 | * @return string 31 | */ 32 | protected function getClass($type) { 33 | $elements = config('laraform.elements'); 34 | 35 | if (!empty($elements) && array_key_exists($type, $elements)) { 36 | return $elements[$type]; 37 | } 38 | 39 | return __NAMESPACE__ . '\\' . str_replace('-', '', ucwords($type, '-')) . 'Element'; 40 | } 41 | } -------------------------------------------------------------------------------- /src/Elements/HiddenElement.php: -------------------------------------------------------------------------------- 1 | secret = $schema['secret']; 40 | } 41 | } 42 | 43 | /** 44 | * Load element data from target 45 | * 46 | * @param object $target 47 | * @return array 48 | */ 49 | public function load($target) 50 | { 51 | $value = $this->database->load($target); 52 | 53 | if ($this->isSecret()) { 54 | $value[$this->attribute] = $this->encrypt($value[$this->attribute]); 55 | } 56 | 57 | return $value; 58 | } 59 | 60 | /** 61 | * Fill element data to target 62 | * 63 | * @param object $target 64 | * @param array $data 65 | * @param bollean $emptyOnNull 66 | * @return void 67 | */ 68 | public function fill($target, $data, $emptyOnNull = true) 69 | { 70 | if (!$this->presentedIn($data)) { 71 | return; 72 | } 73 | 74 | if ($this->isSecret()) { 75 | $data[$this->name] = $data[$this->name] 76 | ? $this->decrypt($data[$this->name]) 77 | : $data[$this->name]; 78 | } 79 | 80 | $this->database->fill($target, $data, $emptyOnNull); 81 | } 82 | 83 | /** 84 | * Empty element's value on target 85 | * 86 | * @param object $target 87 | * @return void 88 | */ 89 | public function empty($target) 90 | { 91 | // do not empty keys 92 | } 93 | 94 | /** 95 | * Save freshly inserted keys 96 | * 97 | * @param object $target 98 | * @return array 99 | */ 100 | public function getNewKeys($target) 101 | { 102 | if ($target === null) { 103 | return; 104 | } 105 | 106 | $key = $this->database->getNewKeys($target); 107 | 108 | if (!$key) { 109 | return; 110 | } 111 | 112 | return $this->isSecret() ? $this->encrypt($key) : $key; 113 | } 114 | 115 | /** 116 | * Return value of secret 117 | * 118 | * @return boolean 119 | */ 120 | public function isSecret() 121 | { 122 | if (env('APP_ENV') === 'local') { 123 | return false; 124 | } 125 | 126 | return $this->secret; 127 | } 128 | 129 | /** 130 | * Encrypt value 131 | * 132 | * @param mixed $value 133 | * @return string 134 | */ 135 | protected function encrypt($value) 136 | { 137 | return Hash::encode($value); 138 | } 139 | 140 | /** 141 | * Decrypt value 142 | * 143 | * @param mixed $value 144 | * @return string 145 | */ 146 | protected function decrypt($value) 147 | { 148 | return Hash::decode($value); 149 | } 150 | } -------------------------------------------------------------------------------- /src/Elements/MetaElement.php: -------------------------------------------------------------------------------- 1 | schema; 86 | 87 | $schema['component'] = $this->getComponent(); 88 | 89 | return $schema; 90 | } 91 | } -------------------------------------------------------------------------------- /src/Elements/TextElement.php: -------------------------------------------------------------------------------- 1 | listeners[$event][] = $callback; 26 | } 27 | 28 | /** 29 | * Fire and event 30 | * 31 | * @param string $event 32 | * @return void 33 | */ 34 | public function fire($event) 35 | { 36 | if (!isset($this->listeners[$event])) { 37 | return; 38 | } 39 | 40 | foreach ($this->listeners[$event] as $listener) { 41 | call_user_func($listener); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Laraform.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | namespace Laraform; 10 | 11 | use Laraform\Authorization\AuthorizationBuilder; 12 | use Laraform\Elements\Factory as ElementFactory; 13 | use Laraform\Event\Event; 14 | use Laraform\Validation\Validation; 15 | use Laraform\User\UserBuilder; 16 | use Laraform\Support\Arr; 17 | use Laraform\Database\DatabaseBuilder; 18 | use Laraform\Support\Hash; 19 | 20 | class Laraform implements \JsonSerializable 21 | { 22 | /** 23 | * Schema of form 24 | * 25 | * @var array 26 | */ 27 | public $schema; 28 | 29 | /** 30 | * Vue component to render 31 | * 32 | * @var array 33 | */ 34 | public $component = 'laraform'; 35 | 36 | /** 37 | * Vuex store (state) path 38 | * 39 | * @var string 40 | */ 41 | public $storePath; 42 | 43 | /** 44 | * Name of model class 45 | * 46 | * Eg: App\User::class 47 | * 48 | * @var string 49 | */ 50 | public $model; 51 | 52 | /** 53 | * Name of primary key 54 | * 55 | * @var string 56 | */ 57 | public $primaryKey = 'id'; 58 | 59 | /** 60 | * Theme for the form 61 | * 62 | * @var array 63 | */ 64 | public $theme; 65 | 66 | /** 67 | * Column sizes definition 68 | * 69 | * @var array 70 | */ 71 | public $columns = null; 72 | 73 | /** 74 | * CSS class of form 75 | * 76 | * @var string 77 | */ 78 | public $class; 79 | 80 | /** 81 | * Theme classes override 82 | * 83 | * @var array 84 | */ 85 | public $classes; 86 | 87 | /** 88 | * Whether label should render for elements 89 | * without label defined 90 | * 91 | * @var boolean 92 | */ 93 | public $labels; 94 | 95 | /** 96 | * Whether form errors should be displayed 97 | * above the form 98 | * 99 | * @var boolean 100 | */ 101 | public $formErrors; 102 | 103 | /** 104 | * Form layout 105 | * 106 | * @var string 107 | */ 108 | public $layout; 109 | 110 | /** 111 | * Form buttons 112 | * 113 | * @var array 114 | */ 115 | public $buttons; 116 | 117 | /** 118 | * Additional "data" props to set for the form. 119 | * 120 | * @var array 121 | */ 122 | public $with = []; 123 | 124 | /** 125 | * Overwrite default validation messages 126 | * 127 | * Eg.: 128 | * [ 129 | * 'required' => 'This field is required' 130 | * ] 131 | * 132 | * @var bool 133 | */ 134 | public $messages = []; 135 | 136 | /** 137 | * Locale of the form 138 | * 139 | * Defaults to app locale 140 | * 141 | * @var string 142 | */ 143 | public $locale; 144 | 145 | /** 146 | * Entpoint to where the form will be submitted 147 | * 148 | * Default: config('laraform.endpoint') 149 | * 150 | * @var string 151 | */ 152 | public $endpoint; 153 | 154 | /** 155 | * Method how the form should be submitted 156 | * 157 | * Default: config('laraform.method') 158 | * 159 | * @var string 160 | */ 161 | public $method; 162 | 163 | /** 164 | * Validated form on these events 165 | * 166 | * Default: config('laraform.validateOn') 167 | * 168 | * @var string 169 | */ 170 | public $validateOn; 171 | 172 | /** 173 | * Name of attribute on User model which returns the roles 174 | * 175 | * @var string 176 | */ 177 | public $rolesAttribute; 178 | 179 | /** 180 | * Auth guard to use 181 | * 182 | * @var string 183 | */ 184 | public $guard; 185 | 186 | /** 187 | * List of permissions 188 | * 189 | * @var array 190 | */ 191 | public $permissions = []; 192 | 193 | /** 194 | * Key of current entity 195 | * 196 | * @var integer 197 | */ 198 | protected $key; 199 | 200 | /** 201 | * Determine if form is invalid 202 | * 203 | * @var boolean 204 | */ 205 | protected $invalid = false; 206 | 207 | /** 208 | * Determine if form has been validated 209 | * 210 | * @var boolean 211 | */ 212 | protected $validated = false; 213 | 214 | /** 215 | * Current data 216 | * 217 | * @var array 218 | */ 219 | protected $data = []; 220 | 221 | /** 222 | * Contains any data that has been generated 223 | * during processing data, like new IDs. Can 224 | * be used to forward to the frontend to up- 225 | * date values. 226 | * 227 | * @var array 228 | */ 229 | protected $updates = []; 230 | 231 | /** 232 | * Current elements 233 | * 234 | * @var Laraform\Contracts\Elements\Element[] 235 | */ 236 | protected $elements = []; 237 | 238 | /** 239 | * Database instance 240 | * 241 | * @var Laraform\Contracts\Database\Database 242 | */ 243 | protected $database; 244 | 245 | /** 246 | * Authorization instance 247 | * 248 | * @var Laraform\Contracts\Authorization\Authorization 249 | */ 250 | protected $authorization; 251 | 252 | /** 253 | * User instance 254 | * 255 | * @var Laraform\Contracts\User\user 256 | */ 257 | protected $user; 258 | 259 | /** 260 | * Validation instance 261 | * 262 | * @var Laraform\Contracts\Validation\Validation 263 | */ 264 | protected $validation; 265 | 266 | /** 267 | * Event dispatcher instance 268 | * 269 | * @var Laraform\Contracts\Event\Dispatcher 270 | */ 271 | protected $event; 272 | 273 | /** 274 | * Element factory instance 275 | * 276 | * @var Laraform\Elements\Factory 277 | */ 278 | protected $elementFactory; 279 | 280 | /** 281 | * Return new Laraform instance 282 | * 283 | * @param AuthorizationBuilder $authorizationBuilder 284 | * @param UserBuilder $userBuilder 285 | * @param Validation $validation 286 | * @param Event $event 287 | * @param ElementFactory $elementFactory 288 | * @param DatabaseBuilder $databaseBuilder 289 | */ 290 | public function __construct( 291 | AuthorizationBuilder $authorizationBuilder, 292 | UserBuilder $userBuilder, 293 | Validation $validation, 294 | Event $event, 295 | ElementFactory $elementFactory, 296 | DatabaseBuilder $databaseBuilder 297 | ) { 298 | if (method_exists($this, 'boot')) { 299 | app()->call([$this, 'boot']); 300 | } 301 | 302 | $this->setSchema(); 303 | 304 | $this->database = $databaseBuilder 305 | ->setForm($this) 306 | ->build(); 307 | 308 | $this->validation = $validation; 309 | $this->event = $event; 310 | $this->elementFactory = $elementFactory; 311 | 312 | $this->setElements(); 313 | } 314 | 315 | /** 316 | * Validate form 317 | * 318 | * @return void 319 | */ 320 | public function validate() 321 | { 322 | $this->fire('validating'); 323 | 324 | $this->validation->validate($this->elements, $this->messages); 325 | 326 | $this->validated = true; 327 | 328 | if ($this->validation->fails()) { 329 | $this->invalid = true; 330 | } 331 | 332 | $this->fire('validated'); 333 | } 334 | 335 | /** 336 | * Load data to form 337 | * 338 | * @param array|integer $data 339 | * @return bool 340 | */ 341 | public function load($data) 342 | { 343 | if (!in_array(gettype($data), ['integer', 'array'])) { 344 | throw new \InvalidArgumentException('Invalid argument type: ' . gettype($data)); 345 | } 346 | 347 | if (is_numeric($data)) { 348 | return $this->loadByKey($data); 349 | } 350 | 351 | $this->fire('loading'); 352 | 353 | $this->setData($data); 354 | 355 | $this->fire('loaded'); 356 | 357 | return $this; 358 | } 359 | 360 | /** 361 | * Load data by key 362 | * 363 | * @param integer $key 364 | * @return bool 365 | */ 366 | public function loadByKey($key) 367 | { 368 | $this->setKey($key); 369 | 370 | $this->fire('loading'); 371 | 372 | $this->setData($this->database->load($key)); 373 | 374 | $this->fire('loaded'); 375 | 376 | return $this; 377 | } 378 | 379 | /** 380 | * Save data 381 | * 382 | * @return void 383 | */ 384 | public function save() 385 | { 386 | $this->fire('saving'); 387 | 388 | $this->hasKey() ? $this->update() : $this->insert(); 389 | 390 | $this->fire('saved'); 391 | 392 | return true; 393 | } 394 | 395 | /** 396 | * Insert new data 397 | * 398 | * @return void 399 | */ 400 | private function insert() 401 | { 402 | $this->fire('inserting'); 403 | 404 | $this->database->insert($this->data); 405 | 406 | $this->setKeysFromInserted(); 407 | $this->addKeysToUpdates(); 408 | 409 | $this->fire('inserted'); 410 | } 411 | 412 | /** 413 | * Update data 414 | * 415 | * @return void 416 | */ 417 | private function update() 418 | { 419 | $this->fire('updating'); 420 | 421 | $this->database->update($this->data, $this->key); 422 | $this->addKeysToUpdates(); 423 | 424 | $this->fire('updated'); 425 | } 426 | 427 | /** 428 | * Renders form DOM element 429 | * 430 | * @return string 431 | */ 432 | public function render() { 433 | $model = $this->storePath ? "store-path=\"{$this->storePath}\"" : ''; 434 | $form = ":form=\"{$this->toProp()}\""; 435 | 436 | return "<{$this->getComponent()} $form $model>getComponent()}>"; 437 | } 438 | 439 | /** 440 | * Transforms form to Vue property 441 | * 442 | * @return string 443 | */ 444 | public function toProp() { 445 | return htmlspecialchars(json_encode($this, true)); 446 | } 447 | 448 | /** 449 | * Add an event listener 450 | * 451 | * @param string $event 452 | * @param callable $callback 453 | * @return void 454 | */ 455 | public function on($event, $callback) 456 | { 457 | $this->event->listen($event, $callback); 458 | } 459 | 460 | /** 461 | * Make a callable method from method name 462 | * 463 | * @param string $method 464 | * @return callable 465 | */ 466 | protected function makeCallableMethod($method) 467 | { 468 | return function () use ($method) { 469 | return $this->$method(); 470 | }; 471 | } 472 | 473 | /** 474 | * Fire an event 475 | * 476 | * @param string $event 477 | * @return any|void 478 | */ 479 | public function fire($event) 480 | { 481 | if ($result = $this->event->fire($event)) { 482 | return $result; 483 | } 484 | 485 | if (method_exists($this, $event)) { 486 | if ($result = $this->$event()) { 487 | return $result; 488 | } 489 | } 490 | } 491 | 492 | /** 493 | * Creates a fail response 494 | * 495 | * @param string $message 496 | * @param array $payload 497 | * @return void 498 | */ 499 | public function fail($message, $payload = []) 500 | { 501 | return response([ 502 | 'status' => 'fail', 503 | 'messages' => [$message], 504 | 'payload' => $payload 505 | ]); 506 | } 507 | 508 | /** 509 | * Creates a success response 510 | * 511 | * @param string $message 512 | * @param array $payload 513 | * @return void 514 | */ 515 | public function success($message = null, $payload = []) 516 | { 517 | $messages = []; 518 | 519 | if ($message) { 520 | $messages[] = $message; 521 | } 522 | 523 | return response([ 524 | 'status' => 'success', 525 | 'messages' => $messages, 526 | 'payload' => $payload 527 | ]); 528 | } 529 | 530 | /** 531 | * Set the value of data 532 | * 533 | * @param array $data 534 | * @return void 535 | */ 536 | public function setData(array $data) 537 | { 538 | $this->data = $data; 539 | 540 | $this->elements->setData($data); 541 | } 542 | 543 | /** 544 | * Get the value of elements 545 | * 546 | * @return Laraform\Contracts\Elements\Element 547 | */ 548 | public function getElements() 549 | { 550 | return $this->elements; 551 | } 552 | 553 | /** 554 | * Return the entity based on current key 555 | * 556 | * @return object 557 | */ 558 | public function getEntity() 559 | { 560 | return $this->hasKey() ? $this->database->find($this->key) : null; 561 | } 562 | 563 | /** 564 | * Get the value of key 565 | * 566 | * @return integer 567 | */ 568 | public function getKey() 569 | { 570 | return $this->key; 571 | } 572 | 573 | /** 574 | * Get the underlying validator instance 575 | * 576 | * @return Illuminate\Validation\Validator 577 | */ 578 | public function getValidator() 579 | { 580 | return $this->validation->validator->validator; 581 | } 582 | 583 | /** 584 | * Set the value of key 585 | * 586 | * @param integer $key 587 | * @return void 588 | */ 589 | public function setKey($key) 590 | { 591 | $this->key = $key; 592 | } 593 | 594 | /** 595 | * Determine if key is set 596 | * 597 | * @return boolean 598 | */ 599 | public function hasKey() 600 | { 601 | return !empty($this->key); 602 | } 603 | 604 | /** 605 | * Determine if model is set 606 | * 607 | * @return boolean 608 | */ 609 | public function hasModel() 610 | { 611 | return !empty($this->model); 612 | } 613 | 614 | /** 615 | * Determine if form is invalid 616 | * 617 | * @return boolean 618 | */ 619 | public function isInvalid() 620 | { 621 | return $this->invalid; 622 | } 623 | 624 | /** 625 | * Determine if form is validated 626 | * 627 | * @return boolean 628 | */ 629 | public function isValidated() 630 | { 631 | return $this->validated; 632 | } 633 | 634 | public function getErrors() { 635 | return $this->validation->getErrors(); 636 | } 637 | 638 | /** 639 | * Set key from current data 640 | * 641 | * @return void 642 | */ 643 | public function setKeyFromData() 644 | { 645 | $key = isset($this->data[$this->primaryKey]) 646 | ? $this->data[$this->primaryKey] 647 | : null; 648 | 649 | if ($key !== null && !is_numeric($key)) { 650 | $key = Hash::decode($key); 651 | } 652 | 653 | $this->key = $key; 654 | } 655 | 656 | /** 657 | * Extend data and set current key 658 | * from last inserted values 659 | * 660 | * @return void 661 | */ 662 | public function setKeysFromInserted() 663 | { 664 | $keys = $this->database->getNewKeys(); 665 | 666 | $this->data = Arr::mergeDeep($this->data, $keys); 667 | 668 | $this->key = $keys[$this->primaryKey] ?? null; 669 | } 670 | 671 | /** 672 | * Add inserted keys to updated 673 | * 674 | * @return void 675 | */ 676 | public function addKeysToUpdates() 677 | { 678 | $keys = $this->database->getNewKeys(); 679 | 680 | if (!empty($keys)) { 681 | $this->addToUpdates($keys); 682 | } 683 | } 684 | 685 | /** 686 | * Merges data with current updates 687 | * 688 | * @param array $updates 689 | * @return void 690 | */ 691 | public function addToUpdates($updates) { 692 | $this->updates = Arr::mergeDeep($this->updates, $updates); 693 | } 694 | 695 | /** 696 | * Returns form udpates 697 | * 698 | * @return [] 699 | */ 700 | public function getUpdates() { 701 | return $this->updates; 702 | } 703 | 704 | /** 705 | * Set schema 706 | * 707 | * @return void 708 | */ 709 | protected function setSchema() 710 | { 711 | if ($this->schema === null) { 712 | $this->schema = $this->schema(); 713 | } 714 | } 715 | 716 | /** 717 | * Set form elements 718 | * 719 | * @return void 720 | */ 721 | public function setElements() 722 | { 723 | $this->elements = $this->elementFactory->make( 724 | $this->schema, 725 | null, 726 | $this->createElementOptions() 727 | ); 728 | } 729 | 730 | /** 731 | * Create options for element 732 | * 733 | * @return void 734 | */ 735 | protected function createElementOptions() 736 | { 737 | return []; 738 | } 739 | 740 | /** 741 | * Set method 742 | * 743 | * @return Laraform 744 | */ 745 | public function setMethod($method) 746 | { 747 | $this->method = $method; 748 | 749 | return $this; 750 | } 751 | 752 | /** 753 | * Set endpoint 754 | * 755 | * @return Laraform 756 | */ 757 | public function setEndpoint($endpoint) 758 | { 759 | $this->endpoint = $endpoint; 760 | 761 | return $this; 762 | } 763 | 764 | /** 765 | * Returns roles attribute name 766 | * 767 | * @return string 768 | */ 769 | public function getRolesAttribute() 770 | { 771 | return $this->rolesAttribute ?: 'role'; 772 | } 773 | 774 | /** 775 | * Returns component name 776 | * 777 | * @return string 778 | */ 779 | public function getComponent() 780 | { 781 | return $this->component ?: config('laraform.component'); 782 | } 783 | 784 | /** 785 | * Generate the form key 786 | * 787 | * @return string 788 | */ 789 | public function getFormKey() 790 | { 791 | return encrypt((new \ReflectionClass($this))->getShortName()); 792 | } 793 | 794 | /** 795 | * Get transformed schema 796 | * 797 | * @param string $side 798 | * @return array 799 | */ 800 | public function getSchema($side) 801 | { 802 | return $this->elements->getSchema($side); 803 | } 804 | 805 | /** 806 | * Retrieving buttons 807 | * 808 | * @return array 809 | */ 810 | public function getButtons() 811 | { 812 | return method_exists($this, 'buttons') 813 | ? $this->buttons() 814 | : $this->buttons; 815 | } 816 | 817 | /** 818 | * Retrieving messages 819 | * 820 | * @return array 821 | */ 822 | public function getMessages() 823 | { 824 | return method_exists($this, 'messages') 825 | ? $this->messages() 826 | : $this->messages; 827 | } 828 | 829 | /** 830 | * Form theme 831 | * 832 | * @return string 833 | */ 834 | public function getTheme() 835 | { 836 | return $this->theme ?: config('laraform.theme'); 837 | } 838 | 839 | /** 840 | * Get form locale 841 | * 842 | * @return string 843 | */ 844 | public function getLocale() 845 | { 846 | return $this->locale ?: config('laraform.locale'); 847 | } 848 | 849 | /** 850 | * Get form default language 851 | * 852 | * @return string 853 | */ 854 | public function getLanguage() 855 | { 856 | return $this->language ?: config('laraform.language'); 857 | } 858 | 859 | /** 860 | * Available languages for form 861 | * 862 | * @return string 863 | */ 864 | public function getLanguages() 865 | { 866 | return $this->languages ?: config('laraform.languages'); 867 | } 868 | 869 | /** 870 | * Submission endpoint 871 | * 872 | * @return string 873 | */ 874 | public function getEndpoint() 875 | { 876 | return $this->endpoint ?: config('laraform.endpoint'); 877 | } 878 | 879 | /** 880 | * Submission method 881 | * 882 | * @return string 883 | */ 884 | public function getMethod() 885 | { 886 | return $this->method ?: config('laraform.method'); 887 | } 888 | 889 | /** 890 | * Returns validateOn 891 | * 892 | * @return string 893 | */ 894 | public function getValidateOn() 895 | { 896 | return $this->validateOn ?: config('laraform.validateOn'); 897 | } 898 | 899 | /** 900 | * Returns columns 901 | * 902 | * @return array 903 | */ 904 | public function getColumns() 905 | { 906 | return $this->columns ?: config('laraform.columns'); 907 | } 908 | 909 | /** 910 | * Retrieves labels 911 | * 912 | * @return string 913 | */ 914 | public function getLabels() 915 | { 916 | return $this->labels !== null ? $this->labels : config('laraform.labels'); 917 | } 918 | 919 | /** 920 | * Retrieves layout 921 | * 922 | * @return string 923 | */ 924 | public function getLayout() 925 | { 926 | return $this->layout ?: config('laraform.layout'); 927 | } 928 | 929 | /** 930 | * Retrieves formErrors 931 | * 932 | * @return string 933 | */ 934 | public function getFormErrors() 935 | { 936 | return $this->formErrors !== null ? $this->formErrors : config('laraform.formErrors'); 937 | } 938 | 939 | /** 940 | * Sets the 'with' property 941 | * 942 | * @return self 943 | */ 944 | public function with(array $with) { 945 | $this->with = $with; 946 | 947 | return $this; 948 | } 949 | 950 | /** 951 | * Serializes form variables when json encoded 952 | * 953 | * @return array 954 | */ 955 | public function jsonSerialize() 956 | { 957 | $form = [ 958 | 'key' => $this->getFormKey(), 959 | 'data' => $this->data, 960 | 'schema' => $this->getSchema('frontend'), 961 | 'theme' => $this->getTheme(), 962 | 'columns' => $this->getColumns(), 963 | 'class' => $this->class, 964 | 'classes' => $this->classes, 965 | 'labels' => $this->getLabels(), 966 | 'layout' => $this->getLayout(), 967 | 'formErrors' => $this->getFormErrors(), 968 | 'buttons' => $this->getButtons(), 969 | 'with' => $this->with, 970 | 'messages' => $this->getMessages(), 971 | 'locale' => $this->getLocale(), 972 | 'endpoint' => $this->getEndpoint(), 973 | 'method' => $this->getMethod(), 974 | 'validateOn' => $this->getValidateOn(), 975 | ]; 976 | 977 | return $form; 978 | } 979 | } -------------------------------------------------------------------------------- /src/LaraformServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->routesAreCached()) { 17 | require __DIR__ . '/routes.php'; 18 | } 19 | 20 | $this->publishes([ 21 | __DIR__ . '/config/laraform.php' => config_path('laraform.php'), 22 | ]); 23 | } 24 | 25 | /** 26 | * Register any application services. 27 | * 28 | * @return void 29 | */ 30 | public function register() 31 | { 32 | $this->app->bind('Laraform\Contracts\Validation\Validator', 'Laraform\Validation\Validator'); 33 | 34 | $this->app->bind('Illuminate\Contracts\Validation\Validator', function($app, $args){ 35 | return app()->makeWith('Illuminate\Validation\Validator', [ 36 | 'data' => [], 37 | 'rules' => [], 38 | ]); 39 | }); 40 | 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Process/AutoProcess.php: -------------------------------------------------------------------------------- 1 | key)); 13 | } 14 | 15 | $form->setData($request->data); 16 | $form->setKeyFromData($request->data); 17 | 18 | if($result = $form->fire('before')) { 19 | return $result; 20 | } 21 | 22 | $form->validate(); 23 | 24 | if ($form->isInvalid()) { 25 | return response([ 26 | 'status' => 'fail', 27 | 'messages' => $form->getErrors(), 28 | 'payload' => [] 29 | ]); 30 | } 31 | 32 | if ($form->hasModel()) { 33 | $form->save(); 34 | } 35 | 36 | if($result = $form->fire('after')) { 37 | return $result; 38 | } 39 | 40 | $updates = $form->getUpdates(); 41 | 42 | return response([ 43 | 'status' => 'success', 44 | 'messages' => [], 45 | 'payload' => count($updates) > 0 ? [ 46 | 'updates' => $updates 47 | ] : [] 48 | ]); 49 | } 50 | } -------------------------------------------------------------------------------- /src/Support/Arr.php: -------------------------------------------------------------------------------- 1 | &$value) { 24 | if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) { 25 | $merged[$key] = self::mergeDeep($merged[$key], $value); 26 | } else if (is_numeric($key)) { 27 | if (!in_array($value, $merged)) 28 | $merged[] = $value; 29 | } else 30 | $merged[$key] = $value; 31 | } 32 | 33 | return $merged; 34 | } 35 | 36 | /** 37 | * Converts an all objects in an array to array 38 | * 39 | * @param array $array 40 | * @return array 41 | */ 42 | public static function forceArray($array) { 43 | return json_decode(json_encode($array), true); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Support/Hash.php: -------------------------------------------------------------------------------- 1 | encode($value); 18 | } 19 | 20 | /** 21 | * Decode value 22 | * 23 | * @param mixed $value 24 | * @return string 25 | */ 26 | public static function decode($value) 27 | { 28 | return (new Hashids(md5(env('APP_KEY')), 10))->decode($value)[0]; 29 | } 30 | } -------------------------------------------------------------------------------- /src/Support/Json.php: -------------------------------------------------------------------------------- 1 | process($request); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Traits/StoresForm.php: -------------------------------------------------------------------------------- 1 | process($request, $this->form()); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Traits/UpdatesForm.php: -------------------------------------------------------------------------------- 1 | process($request, $this->form()); 12 | } 13 | } -------------------------------------------------------------------------------- /src/User/User.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 32 | 33 | $this->guard = $builder->getGuard(); 34 | $this->rolesAttribute = $builder->getRolesAttribute(); 35 | } 36 | 37 | /** 38 | * Determine if the user is logged in 39 | * 40 | * @return void 41 | */ 42 | public function authenticated() 43 | { 44 | return $this->auth()::check(); 45 | } 46 | 47 | /** 48 | * Get the User model 49 | * 50 | * @return Illuminate\Foundation\Auth\User 51 | */ 52 | public function user() 53 | { 54 | return $this->auth()::user(); 55 | } 56 | 57 | /** 58 | * Get the user key 59 | * 60 | * @return int 61 | */ 62 | public function key() 63 | { 64 | return $this->user()->getKey(); 65 | } 66 | 67 | /** 68 | * Get the user role 69 | * 70 | * @return array|null 71 | */ 72 | public function roles() 73 | { 74 | $user = $this->user(); 75 | 76 | if ($user !== null) { 77 | $roles = $user->{$this->rolesAttribute}; 78 | 79 | return is_array($roles) ? $roles : [$roles]; 80 | } 81 | } 82 | 83 | /** 84 | * Return the Auth object 85 | * 86 | * @return Illuminate\Support\Facades\Auth 87 | */ 88 | private function auth() 89 | { 90 | return $this->guard !== null ? $this->auth::guard($this->guard) : $this->auth; 91 | } 92 | } -------------------------------------------------------------------------------- /src/User/UserBuilder.php: -------------------------------------------------------------------------------- 1 | name(); 20 | 21 | $this->addRules($validator, $this->rules(), $name); 22 | $this->addMessages($validator, $this->rules(), $name); 23 | } 24 | } -------------------------------------------------------------------------------- /src/Validation/Strategies/GroupStrategy.php: -------------------------------------------------------------------------------- 1 | children() as $name => $child) { 19 | $child->validate($validator, $prefix); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Validation/Strategies/Strategy.php: -------------------------------------------------------------------------------- 1 | element = $builder->element; 35 | } 36 | 37 | /** 38 | * Validate element 39 | * 40 | * @param Validator $validator 41 | * @param string $prefix 42 | * @return void 43 | */ 44 | public function validate(Validator $validator, $prefix = null) 45 | { 46 | throw new \BadMethodCallException('Unimplemented method'); 47 | } 48 | 49 | /** 50 | * Add rules to validator 51 | * 52 | * @param Validator $validator 53 | * @param mixed $rules 54 | * @param string $name 55 | * @return void 56 | */ 57 | protected function addRules(Validator $validator, $rules, $name) 58 | { 59 | if (!is_array($rules)) { 60 | $rules = explode('|', $rules); 61 | } 62 | 63 | foreach ($rules as $key => $rule) { 64 | switch ($this->getRuleType($rule, $key)) { 65 | case 'callable': 66 | case 'string': 67 | $this->addRule($validator, $rule, $name); 68 | break; 69 | 70 | case 'language': 71 | $this->addRule($validator, $rule, $name . '.' . $key); 72 | break; 73 | 74 | case 'implicit': 75 | $this->addImplicitRule($validator, key($rule), $name, $rule[key($rule)]); 76 | break; 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * Add rule to validator 83 | * 84 | * @param Validator $validator 85 | * @param mixed $rule 86 | * @param string $name 87 | * @return void 88 | */ 89 | protected function addRule(Validator $validator, $rule, $name) 90 | { 91 | if (is_string($rule)) { 92 | $rule = $this->fillWildcards($this->removeDebounce($rule), $name); 93 | } 94 | 95 | if (is_array($rule)) { 96 | $rule = array_map(function($one) use ($name) { 97 | return $this->fillWildcards($this->removeDebounce($one), $name); 98 | }, $rule); 99 | } 100 | 101 | if (in_array($rule, $this->withWildcards)) { 102 | $name = $this->addWildcards($name); 103 | } 104 | 105 | $validator->addRules([ 106 | $name => [$rule] 107 | ]); 108 | } 109 | 110 | /** 111 | * Add implicit rule to validator 112 | * 113 | * @param Validator $validator 114 | * @param string $rule 115 | * @param string $name 116 | * @param array|callable $condition 117 | * @return void 118 | */ 119 | protected function addImplicitRule(Validator $validator, $rule, $name, $condition) 120 | { 121 | if (is_array($condition)) { 122 | $field = $this->fillWildcards($condition[0], $name); 123 | $operator = count($condition) == 3 ? $condition[1] : '='; 124 | $value = count($condition) == 3 ? $condition[2] : $condition[1]; 125 | 126 | $condition = function ($input) use ($field, $operator, $value) { 127 | return $this->compare(Arr::get($input, $field), $value, $operator); 128 | }; 129 | } 130 | 131 | if (in_array($rule, $this->withWildcards)) { 132 | $name = $this->addWildcards($name); 133 | } 134 | 135 | $validator->sometimes($name, $rule, $condition); 136 | } 137 | 138 | /** 139 | * Add messages to validator 140 | * 141 | * @param Validator $validator 142 | * @param mixed $rules 143 | * @param string $name 144 | * @return void 145 | */ 146 | protected function addMessages(Validator $validator, $rules, $name) 147 | { 148 | if (!is_array($rules)) { 149 | $rules = array_map(function($rule) { 150 | return explode(':', $rule)[0]; 151 | }, explode('|', $rules)); 152 | } 153 | 154 | foreach ($rules as $key => $rule) { 155 | switch ($this->getRuleType($rule, $key)) { 156 | case 'callable': 157 | $this->addCallableMessage($validator, $rule, $name); 158 | break; 159 | 160 | case 'string': 161 | $this->addMessage($validator, $rule, $name); 162 | break; 163 | 164 | case 'language': 165 | $this->addMessage($validator, $rule, $name . '.' . $key); 166 | break; 167 | 168 | case 'implicit': 169 | $this->addMessage($validator, key($rule), $name, $rule[key($rule)]); 170 | break; 171 | } 172 | } 173 | } 174 | 175 | /** 176 | * Add message to validator 177 | * 178 | * @param Validator $validator 179 | * @param string $rule 180 | * @param string $name 181 | * @return void 182 | */ 183 | protected function addMessage(Validator $validator, $rule, $name) 184 | { 185 | $message = $this->getMessage($rule); 186 | 187 | if ($message) { 188 | $validator->addCustomMessage($name, $rule, $message); 189 | } 190 | } 191 | 192 | /** 193 | * Add message to validator 194 | * 195 | * @param Validator $validator 196 | * @param Rule $rule 197 | * @param string $name 198 | * @return void 199 | */ 200 | protected function addCallableMessage(Validator $validator, Rule $rule, $name) 201 | { 202 | $message = (new $rule)->message(); 203 | $ruleName = lcfirst((new \ReflectionClass($rule))->getShortName()); 204 | 205 | if ($message) { 206 | $validator->addCustomMessage($name, $ruleName, $message); 207 | } 208 | } 209 | 210 | /** 211 | * Fill asterix values with concrete indexes 212 | * 213 | * @param string $fillable - string to be filled 214 | * @param string $fill - string to get indexes from 215 | * @return string 216 | */ 217 | protected function fillWildcards($fillable, $fill) 218 | { 219 | preg_match('/\.[0-9]/', $fill, $matches); 220 | 221 | if (count($matches) == 0) { 222 | return $fillable; 223 | } 224 | 225 | return vsprintf(str_replace('.*', '%s', $fillable), $matches); 226 | } 227 | 228 | /** 229 | * Add wildcard instead of the last index 230 | * 231 | * @param string $name 232 | * @return string 233 | */ 234 | protected function addWildcards($name) 235 | { 236 | return preg_replace('/\d+(?!\d+)/', '*', $name); 237 | } 238 | 239 | /** 240 | * Compare two values with a given operator 241 | * 242 | * @param mixed $first 243 | * @param mixed $second 244 | * @param string $operator 245 | * @return bool 246 | */ 247 | protected function compare ($first, $second, $operator) { 248 | switch ($operator) { 249 | case "=": 250 | return $first == $second; 251 | case "!=": 252 | return $first != $second; 253 | case ">=": 254 | return $first >= $second; 255 | case "<=": 256 | return $first <= $second; 257 | case ">": 258 | return $first > $second; 259 | case "<": 260 | return $first < $second; 261 | } 262 | 263 | throw new \InvalidArgumentException('Unknown operator:' . $operator); 264 | } 265 | 266 | /** 267 | * Determine the type of rule 268 | * 269 | * @param mixed $rule 270 | * @param mixed $key 271 | * @return string 272 | */ 273 | protected function getRuleType($rule, $key) 274 | { 275 | if (is_numeric($key)) { 276 | if (is_array($rule)) { 277 | return 'implicit'; 278 | } elseif ($rule instanceof Rule) { 279 | return 'callable'; 280 | } else { 281 | return 'string'; 282 | } 283 | } else { 284 | return 'language'; 285 | } 286 | } 287 | 288 | /** 289 | * Removes debounce param from rule 290 | * 291 | * @param [string] $rule 292 | * @return string 293 | */ 294 | protected function removeDebounce($rule) { 295 | if (!is_string($rule)) { 296 | return $rule; 297 | } 298 | 299 | return preg_replace('/[:,]?debounce=\d*[^,]/', '', $rule); 300 | } 301 | 302 | /** 303 | * Get custom message for rule 304 | * 305 | * @return string 306 | */ 307 | public function getMessage($rule) 308 | { 309 | return $this->element->getMessage($rule); 310 | } 311 | 312 | /** 313 | * Name of element 314 | * 315 | * @return string 316 | */ 317 | public function name() 318 | { 319 | return $this->element->name; 320 | } 321 | 322 | /** 323 | * Rules of element 324 | * 325 | * @return string 326 | */ 327 | public function rules() 328 | { 329 | return $this->element->getRules(); 330 | } 331 | 332 | /** 333 | * Children of element 334 | * 335 | * @return Laraform\Contracts\Elements\Element[] 336 | */ 337 | public function children() 338 | { 339 | return $this->element->children; 340 | } 341 | } -------------------------------------------------------------------------------- /src/Validation/StrategyBuilder.php: -------------------------------------------------------------------------------- 1 | \Laraform\Validation\Strategies\DefaultStrategy::class, 23 | 'group' => \Laraform\Validation\Strategies\GroupStrategy::class, 24 | ]; 25 | 26 | /** 27 | * Return new Strategy instance 28 | * 29 | * @return Laraform\Contracts\Validation\Strategy 30 | */ 31 | public function build() 32 | { 33 | $validationStrategy = $this->element->validateAs ?: $this->element->behaveAs; 34 | 35 | if (!array_key_exists($validationStrategy, $this->validators)) { 36 | $validationStrategy = 'default'; 37 | } 38 | 39 | return app()->makeWith($this->validators[$validationStrategy], [ 40 | 'builder' => $this 41 | ]); 42 | } 43 | 44 | /** 45 | * Get the value of element 46 | */ 47 | public function getElement() 48 | { 49 | return $this->element; 50 | } 51 | 52 | /** 53 | * Set the value of element 54 | * 55 | * @return self 56 | */ 57 | public function setElement(Element $element) 58 | { 59 | $this->element = $element; 60 | 61 | return $this; 62 | } 63 | } -------------------------------------------------------------------------------- /src/Validation/Validation.php: -------------------------------------------------------------------------------- 1 | validator = $validator; 26 | } 27 | 28 | /** 29 | * Validates elements against given data 30 | * 31 | * @param Element $elements 32 | * @param array $messages 33 | * @return void 34 | */ 35 | public function validate(Element $elements, array $messages) 36 | { 37 | $this->validator->validate($elements, $messages); 38 | } 39 | 40 | /** 41 | * Determine if validation fails 42 | * 43 | * @return boolean 44 | */ 45 | public function fails() 46 | { 47 | return $this->validator->fails(); 48 | } 49 | 50 | /** 51 | * Return the value of errors 52 | * 53 | * @return array 54 | */ 55 | public function getErrors() 56 | { 57 | $errors = []; 58 | 59 | // Filter errors to quarantee that only one 60 | // of each is represented (required because 61 | // of multivalue validators) 62 | foreach ($this->validator->errors() as $error) { 63 | if (!in_array($error, $errors)) { 64 | $errors[] = $error; 65 | } 66 | } 67 | 68 | return $errors; 69 | } 70 | } -------------------------------------------------------------------------------- /src/Validation/Validator.php: -------------------------------------------------------------------------------- 1 | validator = $validator; 26 | $this->validator->setPresenceVerifier(app('validation.presence')); 27 | } 28 | 29 | /** 30 | * Perform validation 31 | * 32 | * @param Element $elements 33 | * @param array $messages 34 | * @return void 35 | */ 36 | public function validate(Element $elements, array $messages) 37 | { 38 | $this->setData($elements->getValidationData()); 39 | 40 | $this->addMessages($messages); 41 | 42 | $elements->validate($this); 43 | } 44 | 45 | /** 46 | * Set data for validator 47 | * 48 | * @param array $data 49 | * @return void 50 | */ 51 | public function setData(array $data) 52 | { 53 | $this->validator->setData($data); 54 | } 55 | 56 | /** 57 | * Add rules for validator 58 | * 59 | * @param array $rules 60 | * @return void 61 | */ 62 | public function addRules(array $rules) 63 | { 64 | $this->validator->addRules($rules); 65 | } 66 | 67 | /** 68 | * Add custom message for validator 69 | * 70 | * @param string $attribute 71 | * @param string $rule 72 | * @param string $message 73 | * @return void 74 | */ 75 | public function addCustomMessage($attribute, $rule, $message) 76 | { 77 | $this->addMessage($attribute . '.' . $rule, $message); 78 | } 79 | 80 | /** 81 | * Add messages for validator 82 | * 83 | * @param array $messages 84 | * @return void 85 | */ 86 | protected function addMessages($messages) 87 | { 88 | foreach ($messages as $rule => $message) { 89 | $this->addMessage($rule, $message); 90 | } 91 | } 92 | 93 | /** 94 | * Add message for validator 95 | * 96 | * @param string $key 97 | * @param string $message 98 | * @return void 99 | */ 100 | public function addMessage($key, $message) 101 | { 102 | $this->validator->setCustomMessages(array_merge($this->validator->customMessages, [ 103 | $key => $message 104 | ])); 105 | } 106 | 107 | /** 108 | * Set implicit rule for validator 109 | * 110 | * @param string $attribute 111 | * @param mixed $rules 112 | * @param callable $condition 113 | * @return void 114 | */ 115 | public function sometimes($attribute, $rules, callable $condition) 116 | { 117 | $this->validator->sometimes($attribute, $rules, $condition); 118 | } 119 | 120 | /** 121 | * Determine if the validation fails 122 | * 123 | * @return bool 124 | */ 125 | public function fails() 126 | { 127 | return $this->validator->fails(); 128 | } 129 | 130 | /** 131 | * Return errors of validation 132 | * 133 | * @return void 134 | */ 135 | public function errors() 136 | { 137 | return $this->validator->errors()->all(); 138 | } 139 | } -------------------------------------------------------------------------------- /src/config/laraform.php: -------------------------------------------------------------------------------- 1 | 'App\\Forms', 14 | 15 | /* 16 | |-------------------------------------------------------------------------- 17 | | Vue component 18 | |-------------------------------------------------------------------------- 19 | | 20 | | Default Vue component. 21 | | 22 | */ 23 | 'component' => 'laraform', 24 | 25 | /* 26 | |-------------------------------------------------------------------------- 27 | | Theme 28 | |-------------------------------------------------------------------------- 29 | | 30 | | Default theme. 31 | | 32 | */ 33 | 'theme' => 'default', 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Theme 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Default form layout. If `false` no layout will be used. 41 | | 42 | */ 43 | 'layout' => false, 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | Labels 48 | |-------------------------------------------------------------------------- 49 | | 50 | | Determines if the elements which do not have a `label` option defined 51 | | should have a label DOM element rendered. 52 | | 53 | */ 54 | 'labels' => false, 55 | 56 | /* 57 | |-------------------------------------------------------------------------- 58 | | Form Errors 59 | |-------------------------------------------------------------------------- 60 | | 61 | | Determines if errors should be displayed above form. 62 | | 63 | */ 64 | 'formErrors' => true, 65 | 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | Columns 69 | |-------------------------------------------------------------------------- 70 | | 71 | | Default column settings. 72 | | 73 | */ 74 | 'columns' => [ 75 | 'element' => 12, 76 | 'label' => 12, 77 | 'field' => 12 78 | ], 79 | 80 | /* 81 | |-------------------------------------------------------------------------- 82 | | Locale 83 | |-------------------------------------------------------------------------- 84 | | 85 | | Default locale. 86 | | 87 | */ 88 | 'locale' => 'en_US', 89 | 90 | /* 91 | |-------------------------------------------------------------------------- 92 | | Validate On 93 | |-------------------------------------------------------------------------- 94 | | 95 | | When user inputs should be validated. 96 | | 97 | | Possible values: 98 | | submit: upon form submission 99 | | change: instantly upon user input 100 | | step: before moving to the next step when using Wizard 101 | | 102 | */ 103 | 'validateOn' => 'submit|change', 104 | 105 | /* 106 | |-------------------------------------------------------------------------- 107 | | Endpoint 108 | |-------------------------------------------------------------------------- 109 | | 110 | | Default endpoint where the form should submit. 111 | | 112 | */ 113 | 'endpoint' => '/laraform/process', 114 | 115 | /* 116 | |-------------------------------------------------------------------------- 117 | | Method 118 | |-------------------------------------------------------------------------- 119 | | 120 | | Default method how the form should be submitted. 121 | | 122 | */ 123 | 'method' => 'POST', 124 | 125 | /* 126 | |-------------------------------------------------------------------------- 127 | | Elements 128 | |-------------------------------------------------------------------------- 129 | | 130 | | A list of custom elements to be added to Laraform. 131 | | 132 | | eg. [ 133 | | 'custom' => App\Elements\CustomElement::class, 134 | | ] 135 | | 136 | */ 137 | 'elements' => [], 138 | ]; 139 | -------------------------------------------------------------------------------- /src/routes.php: -------------------------------------------------------------------------------- 1 | 'web'], function () { 4 | Route::post('/laraform/process', '\Laraform\Controllers\FormController@process'); 5 | }); --------------------------------------------------------------------------------