From 9f2e246996a82419b3193a1270dea19a5c3061e6 Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Mon, 10 Dec 2018 20:56:53 +0800 Subject: [PATCH] Add outlet basic CRUD php artisan make:crud Outlet --- app/Http/Controllers/OutletController.php | 120 +++++++++++++++ app/Outlet.php | 29 ++++ app/Policies/OutletPolicy.php | 64 ++++++++ app/Providers/AuthServiceProvider.php | 1 + database/factories/OutletFactory.php | 16 ++ .../2018_12_10_125521_create_outlets_table.php | 36 +++++ resources/lang/en/app.php | 18 +++ resources/lang/en/outlet.php | 33 +++++ resources/views/outlets/create.blade.php | 32 ++++ resources/views/outlets/edit.blade.php | 60 ++++++++ resources/views/outlets/index.blade.php | 56 +++++++ resources/views/outlets/show.blade.php | 27 ++++ routes/web.php | 5 + tests/BrowserKitTest.php | 32 ++++ tests/Feature/ManageOutletTest.php | 162 +++++++++++++++++++++ tests/Unit/Models/OutletTest.php | 38 +++++ tests/Unit/Policies/OutletPolicyTest.php | 43 ++++++ 17 files changed, 772 insertions(+) create mode 100644 app/Http/Controllers/OutletController.php create mode 100644 app/Outlet.php create mode 100644 app/Policies/OutletPolicy.php create mode 100644 database/factories/OutletFactory.php create mode 100644 database/migrations/2018_12_10_125521_create_outlets_table.php create mode 100644 resources/lang/en/app.php create mode 100644 resources/lang/en/outlet.php create mode 100644 resources/views/outlets/create.blade.php create mode 100644 resources/views/outlets/edit.blade.php create mode 100644 resources/views/outlets/index.blade.php create mode 100644 resources/views/outlets/show.blade.php create mode 100644 tests/BrowserKitTest.php create mode 100644 tests/Feature/ManageOutletTest.php create mode 100644 tests/Unit/Models/OutletTest.php create mode 100644 tests/Unit/Policies/OutletPolicyTest.php diff --git a/app/Http/Controllers/OutletController.php b/app/Http/Controllers/OutletController.php new file mode 100644 index 0000000..675721e --- /dev/null +++ b/app/Http/Controllers/OutletController.php @@ -0,0 +1,120 @@ +where('name', 'like', '%'.request('q').'%'); + $outlets = $outletQuery->paginate(25); + + return view('outlets.index', compact('outlets')); + } + + /** + * Show the form for creating a new outlet. + * + * @return \Illuminate\View\View + */ + public function create() + { + $this->authorize('create', new Outlet); + + return view('outlets.create'); + } + + /** + * Store a newly created outlet in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Routing\Redirector + */ + public function store(Request $request) + { + $this->authorize('create', new Outlet); + + $newOutlet = $request->validate([ + 'name' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + $newOutlet['creator_id'] = auth()->id(); + + $outlet = Outlet::create($newOutlet); + + return redirect()->route('outlets.show', $outlet); + } + + /** + * Display the specified outlet. + * + * @param \App\Outlet $outlet + * @return \Illuminate\View\View + */ + public function show(Outlet $outlet) + { + return view('outlets.show', compact('outlet')); + } + + /** + * Show the form for editing the specified outlet. + * + * @param \App\Outlet $outlet + * @return \Illuminate\View\View + */ + public function edit(Outlet $outlet) + { + $this->authorize('update', $outlet); + + return view('outlets.edit', compact('outlet')); + } + + /** + * Update the specified outlet in storage. + * + * @param \Illuminate\Http\Request $request + * @param \App\Outlet $outlet + * @return \Illuminate\Routing\Redirector + */ + public function update(Request $request, Outlet $outlet) + { + $this->authorize('update', $outlet); + + $outletData = $request->validate([ + 'name' => 'required|max:60', + 'description' => 'nullable|max:255', + ]); + $outlet->update($outletData); + + return redirect()->route('outlets.show', $outlet); + } + + /** + * Remove the specified outlet from storage. + * + * @param \Illuminate\Http\Request $request + * @param \App\Outlet $outlet + * @return \Illuminate\Routing\Redirector + */ + public function destroy(Request $request, Outlet $outlet) + { + $this->authorize('delete', $outlet); + + $request->validate(['outlet_id' => 'required']); + + if ($request->get('outlet_id') == $outlet->id && $outlet->delete()) { + return redirect()->route('outlets.index'); + } + + return back(); + } +} diff --git a/app/Outlet.php b/app/Outlet.php new file mode 100644 index 0000000..7f68fda --- /dev/null +++ b/app/Outlet.php @@ -0,0 +1,29 @@ + $this->name, 'type' => __('outlet.outlet'), + ]); + $link = ''; + $link .= $this->name; + $link .= ''; + + return $link; + } + + public function creator() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Policies/OutletPolicy.php b/app/Policies/OutletPolicy.php new file mode 100644 index 0000000..305f88c --- /dev/null +++ b/app/Policies/OutletPolicy.php @@ -0,0 +1,64 @@ + 'App\Policies\OutletPolicy', 'App\Model' => 'App\Policies\ModelPolicy', ]; diff --git a/database/factories/OutletFactory.php b/database/factories/OutletFactory.php new file mode 100644 index 0000000..4ff9902 --- /dev/null +++ b/database/factories/OutletFactory.php @@ -0,0 +1,16 @@ +define(Outlet::class, function (Faker $faker) { + + return [ + 'name' => $faker->word, + 'description' => $faker->sentence, + 'creator_id' => function () { + return factory(User::class)->create()->id; + }, + ]; +}); diff --git a/database/migrations/2018_12_10_125521_create_outlets_table.php b/database/migrations/2018_12_10_125521_create_outlets_table.php new file mode 100644 index 0000000..d5f0e0d --- /dev/null +++ b/database/migrations/2018_12_10_125521_create_outlets_table.php @@ -0,0 +1,36 @@ +increments('id'); + $table->string('name', 60); + $table->string('description')->nullable(); + $table->unsignedInteger('creator_id'); + $table->timestamps(); + + $table->foreign('creator_id')->references('id')->on('users')->onDelete('restrict'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('outlets'); + } +} diff --git a/resources/lang/en/app.php b/resources/lang/en/app.php new file mode 100644 index 0000000..a8eaf4b --- /dev/null +++ b/resources/lang/en/app.php @@ -0,0 +1,18 @@ + '#', + 'total' => 'Total', + 'action' => 'Actions', + 'show_detail_title' => 'View :name :type detail', + + // Actions + 'show' => 'View Detail', + 'edit' => 'Edit', + 'delete' => 'Delete', + 'cancel' => 'Cancel', + 'reset' => 'Reset', + 'delete_confirm' => 'Are you sure to delete this?', + 'delete_confirm_button' => 'YES, delete it!', +]; diff --git a/resources/lang/en/outlet.php b/resources/lang/en/outlet.php new file mode 100644 index 0000000..c17982e --- /dev/null +++ b/resources/lang/en/outlet.php @@ -0,0 +1,33 @@ + 'Outlet', + 'list' => 'Outlet List', + 'search' => 'Search Outlet', + 'search_text' => 'Name ...', + 'all' => 'All Outlet', + 'select' => 'Select Outlet', + 'detail' => 'Outlet Detail', + 'not_found' => 'Outlet not found.', + 'empty' => 'Outlet is empty.', + 'back_to_show' => 'Back to Outlet Detail', + 'back_to_index' => 'Back to Outlet List', + + // Actions + 'create' => 'Create new Outlet', + 'created' => 'A new Outlet has been created.', + 'show' => 'View Outlet Detail', + 'edit' => 'Edit Outlet', + 'update' => 'Update Outlet', + 'updated' => 'Outlet data has been updated.', + 'delete' => 'Delete Outlet', + 'delete_confirm' => 'Are you sure to delete this Outlet?', + 'deleted' => 'Outlet has been deleted.', + 'undeleted' => 'Outlet not deleted.', + 'undeleteable' => 'Outlet data cannot be deleted.', + + // Attributes + 'name' => 'Outlet Name', + 'description' => 'Outlet Description', +]; diff --git a/resources/views/outlets/create.blade.php b/resources/views/outlets/create.blade.php new file mode 100644 index 0000000..87644ed --- /dev/null +++ b/resources/views/outlets/create.blade.php @@ -0,0 +1,32 @@ +@extends('layouts.app') + +@section('title', __('outlet.create')) + +@section('content') +
+
+
+
{{ __('outlet.create') }}
+
+ {{ csrf_field() }} +
+
+ + + {!! $errors->first('name', ':message') !!} +
+
+ + + {!! $errors->first('description', ':message') !!} +
+
+ +
+
+
+
+@endsection diff --git a/resources/views/outlets/edit.blade.php b/resources/views/outlets/edit.blade.php new file mode 100644 index 0000000..fd5498a --- /dev/null +++ b/resources/views/outlets/edit.blade.php @@ -0,0 +1,60 @@ +@extends('layouts.app') + +@section('title', __('outlet.edit')) + +@section('content') +
+
+ @if (request('action') == 'delete' && $outlet) + @can('delete', $outlet) +
+
{{ __('outlet.delete') }}
+
+ +

{{ $outlet->name }}

+ +

{{ $outlet->description }}

+ {!! $errors->first('outlet_id', ':message') !!} +
+
+
{{ __('outlet.delete_confirm') }}
+ +
+ @endcan + @else +
+
{{ __('outlet.edit') }}
+
+ {{ csrf_field() }} {{ method_field('patch') }} +
+
+ + + {!! $errors->first('name', ':message') !!} +
+
+ + + {!! $errors->first('description', ':message') !!} +
+
+ +
+
+
+
+@endif +@endsection diff --git a/resources/views/outlets/index.blade.php b/resources/views/outlets/index.blade.php new file mode 100644 index 0000000..cb3ee92 --- /dev/null +++ b/resources/views/outlets/index.blade.php @@ -0,0 +1,56 @@ +@extends('layouts.app') + +@section('title', __('outlet.list')) + +@section('content') +
+
+ @can('create', new App\Outlet) + {{ __('outlet.create') }} + @endcan +
+

{{ __('outlet.list') }} {{ __('app.total') }} : {{ $outlets->total() }} {{ __('outlet.outlet') }}

+
+ +
+
+
+
+
+
+ + +
+ + {{ __('app.reset') }} +
+
+ + + + + + + + + + + @foreach($outlets as $key => $outlet) + + + + + + + @endforeach + +
{{ __('app.table_no') }}{{ __('outlet.name') }}{{ __('outlet.description') }}{{ __('app.action') }}
{{ $outlets->firstItem() + $key }}{!! $outlet->name_link !!}{{ $outlet->description }} + @can('view', $outlet) + {{ __('app.show') }} + @endcan +
+
{{ $outlets->appends(Request::except('page'))->render() }}
+
+
+
+@endsection diff --git a/resources/views/outlets/show.blade.php b/resources/views/outlets/show.blade.php new file mode 100644 index 0000000..7c66f56 --- /dev/null +++ b/resources/views/outlets/show.blade.php @@ -0,0 +1,27 @@ +@extends('layouts.app') + +@section('title', __('outlet.detail')) + +@section('content') +
+
+
+
{{ __('outlet.detail') }}
+
+ + + + + +
{{ __('outlet.name') }}{{ $outlet->name }}
{{ __('outlet.description') }}{{ $outlet->description }}
+
+ +
+
+
+@endsection diff --git a/routes/web.php b/routes/web.php index 12fc04c..b936799 100644 --- a/routes/web.php +++ b/routes/web.php @@ -18,3 +18,8 @@ Route::get('/', function () { Auth::routes(); Route::get('/home', 'HomeController@index')->name('home'); + +/* + * Outlets Routes + */ +Route::resource('outlets', 'OutletController'); diff --git a/tests/BrowserKitTest.php b/tests/BrowserKitTest.php new file mode 100644 index 0000000..c0aa4c7 --- /dev/null +++ b/tests/BrowserKitTest.php @@ -0,0 +1,32 @@ +createUser(); + $this->actingAs($user); + + return $user; + } + + protected function createUser() + { + return factory(User::class)->create(); + } +} diff --git a/tests/Feature/ManageOutletTest.php b/tests/Feature/ManageOutletTest.php new file mode 100644 index 0000000..39b6b44 --- /dev/null +++ b/tests/Feature/ManageOutletTest.php @@ -0,0 +1,162 @@ +create(); + + $this->loginAsUser(); + $this->visitRoute('outlets.index'); + $this->see($outlet->name); + } + + private function getCreateFields(array $overrides = []) + { + return array_merge([ + 'name' => 'Outlet 1 name', + 'description' => 'Outlet 1 description', + ], $overrides); + } + + /** @test */ + public function user_can_create_a_outlet() + { + $this->loginAsUser(); + $this->visitRoute('outlets.index'); + + $this->click(__('outlet.create')); + $this->seeRouteIs('outlets.create'); + + $this->submitForm(__('outlet.create'), $this->getCreateFields()); + + $this->seeRouteIs('outlets.show', Outlet::first()); + + $this->seeInDatabase('outlets', $this->getCreateFields()); + } + + /** @test */ + public function validate_outlet_name_is_required() + { + $this->loginAsUser(); + + // name empty + $this->post(route('outlets.store'), $this->getCreateFields(['name' => ''])); + $this->assertSessionHasErrors('name'); + } + + /** @test */ + public function validate_outlet_name_is_not_more_than_60_characters() + { + $this->loginAsUser(); + + // name 70 characters + $this->post(route('outlets.store'), $this->getCreateFields([ + 'name' => str_repeat('Test Title', 7), + ])); + $this->assertSessionHasErrors('name'); + } + + /** @test */ + public function validate_outlet_description_is_not_more_than_255_characters() + { + $this->loginAsUser(); + + // description 256 characters + $this->post(route('outlets.store'), $this->getCreateFields([ + 'description' => str_repeat('Long description', 16), + ])); + $this->assertSessionHasErrors('description'); + } + + private function getEditFields(array $overrides = []) + { + return array_merge([ + 'name' => 'Outlet 1 name', + 'description' => 'Outlet 1 description', + ], $overrides); + } + + /** @test */ + public function user_can_edit_a_outlet() + { + $this->loginAsUser(); + $outlet = factory(Outlet::class)->create(['name' => 'Testing 123']); + + $this->visitRoute('outlets.show', $outlet); + $this->click('edit-outlet-'.$outlet->id); + $this->seeRouteIs('outlets.edit', $outlet); + + $this->submitForm(__('outlet.update'), $this->getEditFields()); + + $this->seeRouteIs('outlets.show', $outlet); + + $this->seeInDatabase('outlets', $this->getEditFields([ + 'id' => $outlet->id, + ])); + } + + /** @test */ + public function validate_outlet_name_update_is_required() + { + $this->loginAsUser(); + $outlet = factory(Outlet::class)->create(['name' => 'Testing 123']); + + // name empty + $this->patch(route('outlets.update', $outlet), $this->getEditFields(['name' => ''])); + $this->assertSessionHasErrors('name'); + } + + /** @test */ + public function validate_outlet_name_update_is_not_more_than_60_characters() + { + $this->loginAsUser(); + $outlet = factory(Outlet::class)->create(['name' => 'Testing 123']); + + // name 70 characters + $this->patch(route('outlets.update', $outlet), $this->getEditFields([ + 'name' => str_repeat('Test Title', 7), + ])); + $this->assertSessionHasErrors('name'); + } + + /** @test */ + public function validate_outlet_description_update_is_not_more_than_255_characters() + { + $this->loginAsUser(); + $outlet = factory(Outlet::class)->create(['name' => 'Testing 123']); + + // description 256 characters + $this->patch(route('outlets.update', $outlet), $this->getEditFields([ + 'description' => str_repeat('Long description', 16), + ])); + $this->assertSessionHasErrors('description'); + } + + /** @test */ + public function user_can_delete_a_outlet() + { + $this->loginAsUser(); + $outlet = factory(Outlet::class)->create(); + factory(Outlet::class)->create(); + + $this->visitRoute('outlets.edit', $outlet); + $this->click('del-outlet-'.$outlet->id); + $this->seeRouteIs('outlets.edit', [$outlet, 'action' => 'delete']); + + $this->press(__('app.delete_confirm_button')); + + $this->dontSeeInDatabase('outlets', [ + 'id' => $outlet->id, + ]); + } +} diff --git a/tests/Unit/Models/OutletTest.php b/tests/Unit/Models/OutletTest.php new file mode 100644 index 0000000..0f7f384 --- /dev/null +++ b/tests/Unit/Models/OutletTest.php @@ -0,0 +1,38 @@ +create(); + + $title = __('app.show_detail_title', [ + 'name' => $outlet->name, 'type' => __('outlet.outlet'), + ]); + $link = ''; + $link .= $outlet->name; + $link .= ''; + + $this->assertEquals($link, $outlet->name_link); + } + + /** @test */ + public function a_outlet_has_belongs_to_creator_relation() + { + $outlet = factory(Outlet::class)->make(); + + $this->assertInstanceOf(User::class, $outlet->creator); + $this->assertEquals($outlet->creator_id, $outlet->creator->id); + } +} diff --git a/tests/Unit/Policies/OutletPolicyTest.php b/tests/Unit/Policies/OutletPolicyTest.php new file mode 100644 index 0000000..32e4118 --- /dev/null +++ b/tests/Unit/Policies/OutletPolicyTest.php @@ -0,0 +1,43 @@ +createUser(); + $this->assertTrue($user->can('create', new Outlet)); + } + + /** @test */ + public function user_can_view_outlet() + { + $user = $this->createUser(); + $outlet = factory(Outlet::class)->create(); + $this->assertTrue($user->can('view', $outlet)); + } + + /** @test */ + public function user_can_update_outlet() + { + $user = $this->createUser(); + $outlet = factory(Outlet::class)->create(); + $this->assertTrue($user->can('update', $outlet)); + } + + /** @test */ + public function user_can_delete_outlet() + { + $user = $this->createUser(); + $outlet = factory(Outlet::class)->create(); + $this->assertTrue($user->can('delete', $outlet)); + } +}