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')
+
+@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->name }}
+
+
{{ $outlet->description }}
+ {!! $errors->first('outlet_id', '
:message') !!}
+
+
+
{{ __('outlet.delete_confirm') }}
+
+
+ @endcan
+ @else
+
+
+
+
+
+
+@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')
+
+
+
{{ __('outlet.list') }} {{ __('app.total') }} : {{ $outlets->total() }} {{ __('outlet.outlet') }}
+
+
+
+
+
+
+
+
+
+ | {{ __('app.table_no') }} |
+ {{ __('outlet.name') }} |
+ {{ __('outlet.description') }} |
+ {{ __('app.action') }} |
+
+
+
+ @foreach($outlets as $key => $outlet)
+
+ | {{ $outlets->firstItem() + $key }} |
+ {!! $outlet->name_link !!} |
+ {{ $outlet->description }} |
+
+ @can('view', $outlet)
+ {{ __('app.show') }}
+ @endcan
+ |
+
+ @endforeach
+
+
+
{{ $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.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));
+ }
+}