├── .github └── issue_template.md ├── .gitignore ├── .styleci.yml ├── LICENSE ├── README.md ├── composer.json ├── src ├── AppServiceProvider.php ├── app │ ├── Exceptions │ │ └── ContactException.php │ ├── Forms │ │ ├── Builders │ │ │ └── ContactForm.php │ │ └── Templates │ │ │ └── contact.json │ ├── Http │ │ ├── Controllers │ │ │ ├── ContactController.php │ │ │ └── ContactTableController.php │ │ ├── Requests │ │ │ └── ValidateContactRequest.php │ │ └── Resources │ │ │ └── Contact.php │ ├── Models │ │ └── Contact.php │ ├── Tables │ │ ├── Builders │ │ │ └── ContactTable.php │ │ └── Templates │ │ │ └── contacts.json │ └── Traits │ │ ├── Contactable.php │ │ └── Contacts.php ├── config │ └── contacts.php ├── database │ ├── factories │ │ └── ContactFactory.php │ └── migrations │ │ ├── 2017_01_01_147000_create_contacts_table.php │ │ └── 2017_01_01_148000_create_structure_for_contacts.php ├── resources │ └── js │ │ ├── components │ │ └── enso │ │ │ └── contacts │ │ │ ├── Contact.vue │ │ │ ├── ContactForm.vue │ │ │ ├── Contacts.vue │ │ │ └── ContactsCard.vue │ │ ├── pages │ │ └── administration │ │ │ └── contacts │ │ │ └── Index.vue │ │ └── routes │ │ └── administration │ │ └── contacts.js └── routes │ └── api.php └── tests └── features └── ContactTest.php /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | This is a **bug | feature request**. 3 | 4 | 5 | ### Prerequisites 6 | * [ ] Are you running the latest version? 7 | * [ ] Are you reporting to the correct repository? 8 | * [ ] Did you check the documentation? 9 | * [ ] Did you perform a cursory search? 10 | 11 | ### Description 12 | 13 | 14 | ### Steps to Reproduce 15 | 20 | 21 | ### Expected behavior 22 | 23 | 24 | ### Actual behavior 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | .DS_STORE 3 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | enabled: 4 | - strict 5 | - unalign_double_arrow 6 | - phpdoc_order 7 | - phpdoc_separation 8 | 9 | disabled: 10 | - short_array_syntax 11 | 12 | finder: 13 | exclude: 14 | - "public" 15 | - "resources" 16 | - "tests" 17 | name: 18 | - "*.php" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 laravel-enso 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Contacts 3 | 4 | [![License](https://poser.pugx.org/laravel-enso/contacts/license)](https://packagist.org/packages/laravel-enso/contacts) 5 | [![Total Downloads](https://poser.pugx.org/laravel-enso/contacts/downloads)](https://packagist.org/packages/laravel-enso/contacts) 6 | [![Latest Stable Version](https://poser.pugx.org/laravel-enso/contacts/version)](https://packagist.org/packages/laravel-enso/contacts) 7 | 8 | 9 | Contacts dependency for [Laravel Enso](https://github.com/laravel-enso/Enso). 10 | 11 | [![Screenshot](https://laravel-enso.github.io/contacts/screenshots/bulma_024_thumb.png)](https://laravel-enso.github.io/contacts/videos/demo_contacts.webm) 12 | 13 | ### Features 14 | 15 | - allows the management of contact persons, attached to other entities, such as owners 16 | - uses a polymorphic relationship for flexibility when attaching 17 | - contacts can be disabled while still keeping them, for reference 18 | - comes with a `Contacts.vue` VueJS component meant to be included anywhere, and an index page to list all contacts 19 | - uses the [VueComponents](https://github.com/laravel-enso/VueComponents) package in order to load its VueJS dependencies 20 | 21 | 22 | ### Installation steps 23 | 24 | The component is already included in the Enso install and should not require any additional installation steps. 25 | 26 | ### Use 27 | 28 | 1. Define the `'model_alias' => 'App\Model'` mapping in the `config/enso/contacts.php` file 29 | 2. Add the `Contactable` trait in the Model to which you need to add contacts 30 | You can then use the `$model->contacts` relationship 31 | 3. Use the VueJS component in your pages/components 32 | 33 | ``` 34 | 39 | 40 | ``` 41 | 42 | ### Options 43 | The `Contacts.vue` component can be used anywhere, can be integrated into any other component or page, and takes the following parameters: 44 | - `id` - the id of the entity | required 45 | - `type` - string, the entity type, set in the configuration file | required 46 | - `open` - boolean, flag which, if true, makes the component start in the open state | default `false` | (optional) 47 | - `title` - string, title for the component | default 'Contacts' | (optional) 48 | 49 | ### Publishes 50 | 51 | - `php artisan vendor:publish --tag=contacts-config` - the configuration file 52 | - `php artisan vendor:publish --tag=contacts-assets` - the VueJS components 53 | - `php artisan vendor:publish --tag=enso-assets` - a common alias for when wanting to update the VueJS component, 54 | once a newer version is released, can be used with the `--force` flag 55 | - `php artisan vendor:publish --tag=enso-config` - a common alias for when wanting to update the config, 56 | once a newer version is released, can be used with the `--force` flag 57 | 58 | ### Notes 59 | 60 | The [Laravel Enso](https://github.com/laravel-enso/Enso) package comes with this package included. 61 | 62 | Depends on: 63 | - [Datatable](https://github.com/laravel-enso/Datatable) for showing the list of contacts 64 | - [Structure manager](https://github.com/laravel-enso/StructureManager) for the migrations 65 | - [TrackWho](https://github.com/laravel-enso/TrackWho) for keeping track of the users making the changes to each contact 66 | - [VueComponents](https://github.com/laravel-enso/VueComponents) for the accompanying VueJS components 67 | - [Helpers](https://github.com/laravel-enso/Helpers) for the IsActive trait 68 | 69 | 70 | ### Contributions 71 | 72 | are welcome. Pull requests are great, but issues are good too. 73 | 74 | ### License 75 | 76 | This package is released under the MIT license. 77 | 78 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel-enso/contacts", 3 | "description": "Contacts dependency for Larave Enso", 4 | "keywords": [ 5 | "laravel-enso", 6 | "contacts" 7 | ], 8 | "homepage": "https://github.com/laravel-enso/Contacts", 9 | "type": "library", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Mihai Ocneanu", 14 | "email": "mihai.ocneanu@gmail.com", 15 | "homepage": "https://laravel-enso.com", 16 | "role": "Developer" 17 | }, 18 | { 19 | "name": "Adrian Ocneanu", 20 | "email": "aocneanu@gmail.com", 21 | "homepage": "https://laravel-enso.com", 22 | "role": "Developer" 23 | } 24 | ], 25 | "require": { 26 | "php": ">=7.1.0", 27 | "laravel-enso/activitylog": "1.1.*", 28 | "laravel-enso/core": "3.2.*", 29 | "laravel-enso/helpers": "1.9.*", 30 | "laravel-enso/formbuilder": "2.10.*", 31 | "laravel-enso/structuremanager": "2.4.*", 32 | "laravel-enso/vuecomponents": "2.5.*", 33 | "laravel-enso/vuedatatable": "1.7.*" 34 | }, 35 | "autoload": { 36 | "psr-4": { 37 | "LaravelEnso\\Contacts\\": "src/" 38 | } 39 | }, 40 | "extra": { 41 | "laravel": { 42 | "providers": [ 43 | "LaravelEnso\\Contacts\\AppServiceProvider" 44 | ], 45 | "aliases": {} 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadDependencies() 12 | ->publishDependencies(); 13 | } 14 | 15 | private function loadDependencies() 16 | { 17 | $this->loadMigrationsFrom(__DIR__.'/database/migrations'); 18 | 19 | $this->loadRoutesFrom(__DIR__.'/routes/api.php'); 20 | 21 | return $this; 22 | } 23 | 24 | private function publishDependencies() 25 | { 26 | $this->publishes([ 27 | __DIR__.'/config' => config_path('enso'), 28 | ], 'contacts-config'); 29 | 30 | $this->publishes([ 31 | __DIR__.'/config' => config_path('enso'), 32 | ], 'enso-config'); 33 | 34 | $this->publishes([ 35 | __DIR__.'/resources/js' => resource_path('js'), 36 | ], 'contacts-assets'); 37 | 38 | $this->publishes([ 39 | __DIR__.'/resources/js' => resource_path('js'), 40 | ], 'enso-assets'); 41 | 42 | $this->publishes([ 43 | __DIR__.'/database/factories' => database_path('factories'), 44 | ], 'contacts-factory'); 45 | 46 | $this->publishes([ 47 | __DIR__.'/database/factories' => database_path('factories'), 48 | ], 'enso-factories'); 49 | } 50 | 51 | public function register() 52 | { 53 | // 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/Exceptions/ContactException.php: -------------------------------------------------------------------------------- 1 | form = new Form(self::FormPath); 17 | } 18 | 19 | public function create() 20 | { 21 | return $this->form 22 | ->title('Create') 23 | ->actions('store') 24 | ->create(); 25 | } 26 | 27 | public function edit(Contact $contact) 28 | { 29 | return $this->form 30 | ->title('Edit') 31 | ->actions('update') 32 | ->edit($contact); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/Forms/Templates/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "icon": "address-book", 4 | "method": null, 5 | "routePrefix": "core.contacts", 6 | "sections": [{ 7 | "columns": 2, 8 | "fields": [{ 9 | "label": "First Name", 10 | "name": "first_name", 11 | "value": "", 12 | "meta": { 13 | "custom": false, 14 | "type": "input", 15 | "content": "text", 16 | "disabled": false 17 | } 18 | }, { 19 | "label": "Last Name", 20 | "name": "last_name", 21 | "value": null, 22 | "meta": { 23 | "custom": false, 24 | "type": "input", 25 | "content": "text", 26 | "disabled": false 27 | } 28 | }, { 29 | "label": "Email", 30 | "name": "email", 31 | "value": null, 32 | "meta": { 33 | "custom": false, 34 | "type": "input", 35 | "content": "text", 36 | "disabled": false 37 | } 38 | }, { 39 | "label": "Phone", 40 | "name": "phone", 41 | "value": null, 42 | "meta": { 43 | "custom": false, 44 | "type": "input", 45 | "content": "text", 46 | "disabled": false 47 | } 48 | }, { 49 | "label": "Position", 50 | "name": "position", 51 | "value": null, 52 | "meta": { 53 | "custom": false, 54 | "type": "input", 55 | "content": "text", 56 | "disabled": false 57 | } 58 | }, { 59 | "label": "Active", 60 | "name": "is_active", 61 | "value": false, 62 | "meta": { 63 | "type": "input", 64 | "content": "checkbox" 65 | } 66 | }] 67 | }, { 68 | "columns": 1, 69 | "fields": [{ 70 | "label": "Notes", 71 | "name": "obs", 72 | "value": null, 73 | "meta": { 74 | "type": "textarea", 75 | "rows": 3 76 | } 77 | }, { 78 | "label": "", 79 | "name": "contactable_id", 80 | "value": null, 81 | "meta": { 82 | "custom": false, 83 | "type": "input", 84 | "content": "text", 85 | "hidden": true 86 | } 87 | }, { 88 | "label": "", 89 | "name": "contactable_type", 90 | "value": null, 91 | "meta": { 92 | "custom": false, 93 | "type": "input", 94 | "content": "text", 95 | "hidden": true 96 | } 97 | }] 98 | }] 99 | } -------------------------------------------------------------------------------- /src/app/Http/Controllers/ContactController.php: -------------------------------------------------------------------------------- 1 | for($request->validated()) 18 | ->ordered() 19 | ->get() 20 | ); 21 | } 22 | 23 | public function create(ContactForm $form) 24 | { 25 | return ['form' => $form->create()]; 26 | } 27 | 28 | public function store(ValidateContactRequest $request) 29 | { 30 | Contact::create($request->validated()); 31 | 32 | return [ 33 | 'message' => __('The contact was created successfully'), 34 | ]; 35 | } 36 | 37 | public function edit(Contact $contact, ContactForm $form) 38 | { 39 | return ['form' => $form->edit($contact)]; 40 | } 41 | 42 | public function update(ValidateContactRequest $request, Contact $contact) 43 | { 44 | $contact->update($request->validated()); 45 | 46 | return [ 47 | 'message' => __('The contact was updated successfully'), 48 | ]; 49 | } 50 | 51 | public function destroy(Contact $contact) 52 | { 53 | $contact->delete(); 54 | 55 | return [ 56 | 'message' => __('The contact was deleted successfully'), 57 | ]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/app/Http/Controllers/ContactTableController.php: -------------------------------------------------------------------------------- 1 | 'required', 19 | 'contactable_type' => 'required', 20 | ]; 21 | 22 | return $this->method() === 'GET' 23 | ? $rules 24 | : $rules + [ 25 | 'first_name' => 'required|max:50', 26 | 'last_name' => 'required|max:50', 27 | 'email' => 'email|nullable', 28 | 'phone' => 'nullable', 29 | 'position' => 'nullable', 30 | 'obs' => 'nullable', 31 | 'is_active' => 'boolean', 32 | ]; 33 | } 34 | 35 | public function withValidator($validator) 36 | { 37 | $validator->after(function ($validator) { 38 | if (! class_exists($this->contactable_type)) { 39 | throw new ContactException( 40 | 'The "contactable_type" property must be a valid model class' 41 | ); 42 | } 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/Http/Resources/Contact.php: -------------------------------------------------------------------------------- 1 | $this->id, 13 | 'name' => $this->fullName, 14 | 'email' => $this->email, 15 | 'phone' => $this->phone, 16 | 'position' => $this->position, 17 | 'isActive' => $this->is_active, 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/Models/Contact.php: -------------------------------------------------------------------------------- 1 | false]; 19 | 20 | protected $casts = ['is_active' => 'boolean']; 21 | 22 | protected $loggableLabel = 'fullName'; 23 | 24 | protected $loggable = [ 25 | 'first_name', 'last_name', 'phone', 'email', 'position', 26 | 'is_active' => 'active state', 27 | ]; 28 | 29 | public function contactable() 30 | { 31 | return $this->morphTo(); 32 | } 33 | 34 | public function getFullNameAttribute() 35 | { 36 | return trim($this->first_name.' '.$this->last_name); 37 | } 38 | 39 | public function scopeFor($query, array $params) 40 | { 41 | $query->whereContactableId($params['contactable_id']) 42 | ->whereContactableType($params['contactable_type']); 43 | } 44 | 45 | public function scopeOrdered($query) 46 | { 47 | $query->orderBy('created_at', 'desc'); 48 | } 49 | 50 | public function getLoggableMorph() 51 | { 52 | return config('enso.contacts.loggableMorph'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/app/Tables/Builders/ContactTable.php: -------------------------------------------------------------------------------- 1 | contacts()->first() !== null) { 15 | throw new ConflictHttpException( 16 | __('The entity has contacts and cannot be deleted') 17 | ); 18 | } 19 | }); 20 | 21 | self::deleted(function ($model) { 22 | if (config('enso.contacts.onDelete') === 'cascade') { 23 | $model->contacts()->delete(); 24 | } 25 | }); 26 | } 27 | 28 | public function contacts() 29 | { 30 | return $this->morphMany(Contact::class, 'contactable'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/app/Traits/Contacts.php: -------------------------------------------------------------------------------- 1 | hasMany(Contact::class, 'contactable'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/config/contacts.php: -------------------------------------------------------------------------------- 1 | 'cascade', 6 | 'loggableMorph' => [ 7 | 'contactable' => [], 8 | ], 9 | ]; 10 | -------------------------------------------------------------------------------- /src/database/factories/ContactFactory.php: -------------------------------------------------------------------------------- 1 | define(Contact::class, function (Faker $faker) { 7 | return [ 8 | 'contactable_id' => $faker->randomKey, 9 | 'contactable_type' => $faker->word, 10 | 'first_name' => $faker->firstName, 11 | 'last_name' => $faker->lastName, 12 | 'email' => $faker->unique()->safeEmail, 13 | 'phone' => $faker->phoneNumber, 14 | 'position' => $faker->jobTitle, 15 | 'is_active' => $faker->boolean, 16 | ]; 17 | }); 18 | -------------------------------------------------------------------------------- /src/database/migrations/2017_01_01_147000_create_contacts_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 13 | 14 | $table->morphs('contactable'); 15 | 16 | $table->string('first_name'); 17 | $table->string('last_name'); 18 | $table->string('phone')->nullable(); 19 | $table->string('email')->nullable(); 20 | $table->string('position')->nullable(); 21 | $table->text('obs')->nullable(); 22 | 23 | $table->boolean('is_active'); 24 | 25 | $table->timestamps(); 26 | }); 27 | } 28 | 29 | public function down() 30 | { 31 | Schema::dropIfExists('contacts'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/database/migrations/2017_01_01_148000_create_structure_for_contacts.php: -------------------------------------------------------------------------------- 1 | 'core.contacts.index', 'description' => 'Contacts index', 'type' => 0, 'is_default' => false], 9 | ['name' => 'core.contacts.create', 'description' => 'Get create form for contacts', 'type' => 0, 'is_default' => false], 10 | ['name' => 'core.contacts.edit', 'description' => 'Get edit form for contacts', 'type' => 0, 'is_default' => false], 11 | ['name' => 'core.contacts.update', 'description' => 'Update edited contact', 'type' => 1, 'is_default' => false], 12 | ['name' => 'core.contacts.store', 'description' => 'Store newly created contact', 'type' => 1, 'is_default' => false], 13 | ['name' => 'core.contacts.destroy', 'description' => 'Delete contact', 'type' => 1, 'is_default' => false], 14 | ['name' => 'core.contacts.initTable', 'description' => 'Init table for contacts', 'type' => 0, 'is_default' => false], 15 | ['name' => 'core.contacts.tableData', 'description' => 'Get table data for contacts', 'type' => 0, 'is_default' => false], 16 | ['name' => 'core.contacts.exportExcel', 'description' => 'Export excel for contacts', 'type' => 0, 'is_default' => false], 17 | ]; 18 | 19 | protected $menu = [ 20 | 'name' => 'Contacts', 'icon' => 'address-book', 'route' => 'core.contacts.index', 'order_index' => 999, 'has_children' => false, 21 | ]; 22 | 23 | protected $parentMenu = 'Administration'; 24 | } 25 | -------------------------------------------------------------------------------- /src/resources/js/components/enso/contacts/Contact.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 84 | 85 | 93 | 94 | -------------------------------------------------------------------------------- /src/resources/js/components/enso/contacts/ContactForm.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 29 | -------------------------------------------------------------------------------- /src/resources/js/components/enso/contacts/Contacts.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 191 | 192 | 200 | -------------------------------------------------------------------------------- /src/resources/js/components/enso/contacts/ContactsCard.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 99 | 100 | 107 | -------------------------------------------------------------------------------- /src/resources/js/pages/administration/contacts/Index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 54 | -------------------------------------------------------------------------------- /src/resources/js/routes/administration/contacts.js: -------------------------------------------------------------------------------- 1 | const ContactsIndex = () => import('../../pages/administration/contacts/Index.vue'); 2 | 3 | export default { 4 | name: 'core.contacts.index', 5 | path: 'contacts', 6 | component: ContactsIndex, 7 | meta: { 8 | breadcrumb: 'contacts', 9 | title: 'Contacts', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/routes/api.php: -------------------------------------------------------------------------------- 1 | prefix('api/core')->as('core.') 5 | ->namespace('LaravelEnso\Contacts\app\Http\Controllers') 6 | ->group(function () { 7 | Route::prefix('contacts')->as('contacts.') 8 | ->group(function () { 9 | Route::get('initTable', 'ContactTableController@init') 10 | ->name('initTable'); 11 | Route::get('tableData', 'ContactTableController@data') 12 | ->name('tableData'); 13 | Route::get('exportExcel', 'ContactTableController@excel') 14 | ->name('exportExcel'); 15 | }); 16 | 17 | Route::resource('contacts', 'ContactController', ['except' => ['show']]); 18 | }); 19 | -------------------------------------------------------------------------------- /tests/features/ContactTest.php: -------------------------------------------------------------------------------- 1 | withoutExceptionHandling(); 24 | 25 | $this->seed() 26 | ->actingAs(User::first()); 27 | 28 | $this->model = $this->model(); 29 | $this->testModel = factory(Contact::class) 30 | ->make([ 31 | 'contactable_id' => $this->model->id, 32 | 'contactable_type' => get_class($this->model), 33 | ]); 34 | } 35 | 36 | /** @test */ 37 | public function can_get_contacts_index() 38 | { 39 | $this->testModel->save(); 40 | 41 | $this->get(route('core.contacts.index', [ 42 | 'contactable_type' => get_class($this->model), 43 | 'contactable_id' => $this->model->id 44 | ], false)) 45 | ->assertStatus(200) 46 | ->assertJsonFragment(['id' => $this->model->id]); 47 | } 48 | 49 | /** @test */ 50 | public function can_store_contact() 51 | { 52 | $this->post( 53 | route('core.contacts.store', [], false), 54 | $this->testModel->toArray() + [ 55 | '_params' => [ 56 | 'contactable_id' => $this->model->id, 57 | 'contactable_type' => get_class($this->model), 58 | ]] 59 | )->assertStatus(200); 60 | 61 | $contact = Contact::whereFirstName($this->testModel->first_name) 62 | ->first(); 63 | 64 | $this->assertNotNull($contact); 65 | } 66 | 67 | /** @test */ 68 | public function can_update_contact() 69 | { 70 | $this->testModel->save(); 71 | $this->testModel->first_name = 'edited'; 72 | 73 | $this->patch( 74 | route('core.contacts.update', $this->testModel->id, false), 75 | $this->testModel->toArray() 76 | )->assertStatus(200); 77 | 78 | $this->assertEquals('edited', $this->testModel->fresh()->first_name); 79 | } 80 | 81 | /** @test */ 82 | public function can_destroy_contact() 83 | { 84 | $this->testModel->save(); 85 | 86 | $this->delete(route('core.contacts.destroy', $this->testModel->id, false)) 87 | ->assertStatus(200) 88 | ->assertJsonStructure(['message']); 89 | 90 | $this->assertNull($this->testModel->fresh()); 91 | } 92 | 93 | private function model() 94 | { 95 | $this->createTestTable(); 96 | 97 | return ContactTestModel::create(['name' => 'contactable']); 98 | } 99 | 100 | private function createTestTable() 101 | { 102 | Schema::create('contact_test_models', function ($table) { 103 | $table->increments('id'); 104 | $table->string('name'); 105 | $table->timestamps(); 106 | }); 107 | } 108 | } 109 | 110 | class ContactTestModel extends Model 111 | { 112 | use Contactable; 113 | 114 | protected $fillable = ['name']; 115 | } 116 | --------------------------------------------------------------------------------