Browse Source

Added file upload for project

pull/1/head
Nafies Luthfi 8 years ago
parent
commit
cebbee8445
  1. 20
      app/Entities/Projects/File.php
  2. 5
      app/Entities/Projects/Project.php
  3. 77
      app/Http/Controllers/Projects/FilesController.php
  4. 37
      database/migrations/2017_08_03_235706_create_files_table.php
  5. 6
      resources/views/projects/features-export-html.blade.php
  6. 58
      resources/views/projects/files.blade.php
  7. 3
      resources/views/projects/partials/nav-tabs.blade.php
  8. 7
      routes/web/projects.php
  9. 38
      tests/Feature/Projects/UploadFilesTest.php
  10. 8
      tests/TestCase.php
  11. 8
      tests/Unit/Models/ProjectTest.php

20
app/Entities/Projects/File.php

@ -0,0 +1,20 @@
<?php
namespace App\Entities\Projects;
use Illuminate\Database\Eloquent\Model;
class File extends Model
{
protected $fillable = ['fileable_id', 'fileable_type', 'type_id', 'filename', 'title', 'description'];
public function fileable()
{
return $this->morphTo();
}
public function project()
{
return $this->morphTo('fileable', Project::class);
}
}

5
app/Entities/Projects/Project.php

@ -82,4 +82,9 @@ class Project extends Model {
return $overalProgress; return $overalProgress;
} }
public function files()
{
return $this->morphMany(File::class, 'fileable');
}
} }

77
app/Http/Controllers/Projects/FilesController.php

@ -0,0 +1,77 @@
<?php
namespace App\Http\Controllers\Projects;
use App\Entities\Projects\File;
use App\Http\Controllers\Controller;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
class FilesController extends Controller
{
private $fileableTypes = [
'projects' => 'App\Entities\Projects\Project',
];
public function index(Request $request, $fileableId)
{
$fileableType = $request->segment(1); // projects, features
$modelName = $this->getModelName($fileableType);
$modelShortName = $this->getModelShortName($modelName);
$model = $modelName::findOrFail($fileableId);
$files = $model->files;
return view($fileableType.'.files', [$modelShortName => $model, 'files' => $files]);
}
public function create(Request $request, $fileableId)
{
$fileableExist = array_search($request->get('fileable_type'), $this->fileableTypes);
if ($fileableExist) {
$file = $this->proccessPhotoUpload($request->except('_token'), $request->get('fileable_type'), $fileableId);
if ($file->exists)
flash()->success('Upload file berhasil.');
else
flash()->error('Upload file gagal, coba kembali.');
} else
flash()->error('Upload file gagal, coba kembali.');
return back();
}
private function proccessPhotoUpload($data, $fileableType, $fileableId)
{
// dd(get_class_methods($data['files']));
$file = $data['files'];
// $fileName = md5(uniqid(rand(), true)).'.'.$file->getClientOriginalExtension();
$fileName = $file->hashName();
// dd($fileName);
$fileData['fileable_id'] = $fileableId;
$fileData['fileable_type'] = $fileableType;
$fileData['filename'] = $fileName;
$fileData['title'] = $data['title'];
$fileData['description'] = $data['description'];
\DB::beginTransaction();
// dd(is_dir(storage_path('app/public/files')));
$file->storeAs('public/files', $fileName);
// $file->move(storage_path('app/public/files'));
$file = File::create($fileData);
\DB::commit();
return $file;
}
public function getModelName($fileableType)
{
return isset($this->fileableTypes[$fileableType]) ? $this->fileableTypes[$fileableType] : false;
}
public function getModelShortName($modelName)
{
return strtolower((new \ReflectionClass($modelName))->getShortName());
}
}

37
database/migrations/2017_08_03_235706_create_files_table.php

@ -0,0 +1,37 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateFilesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('files', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('fileable_id');
$table->string('fileable_type', 60);
$table->tinyInteger('type_id')->unsigned()->nullable();
$table->string('filename', 60);
$table->string('title', 60);
$table->string('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('files');
}
}

6
resources/views/projects/features-export-html.blade.php

@ -17,15 +17,15 @@
<h1 class="page-header text-center">{{ trans('project.features') }} {{ $project->name }}</h1> <h1 class="page-header text-center">{{ trans('project.features') }} {{ $project->name }}</h1>
@foreach($features as $key => $feature) @foreach($features as $key => $feature)
<h2 class="feature-title">{{ $feature->name }}</h2>
<h2 class="feature-title">{{ 1 + $key }}. {{ $feature->name }}</h2>
<table width="100%" class="table table-condensed table-bordered"> <table width="100%" class="table table-condensed table-bordered">
<tbody> <tbody>
<tr style="background-color: #FFCC00"><th colspan="2">{{ trans('app.description') }}</th></tr>
<tr style="background-color: #ffd298"><th colspan="2">{{ trans('app.description') }}</th></tr>
<tr><td colspan="2">{!! nl2br($feature->description) !!}</td></tr> <tr><td colspan="2">{!! nl2br($feature->description) !!}</td></tr>
@if ($feature->tasks->count()) @if ($feature->tasks->count())
<tr><td colspan="2">&nbsp;</td></tr> <tr><td colspan="2">&nbsp;</td></tr>
<tr style="background-color: #FFCC00">
<tr style="background-color: #ffd298">
<th class="col-md-3">Sub Fitur</th> <th class="col-md-3">Sub Fitur</th>
<th class="col-md-6">{{ trans('app.description') }}</th> <th class="col-md-6">{{ trans('app.description') }}</th>
</tr> </tr>

58
resources/views/projects/files.blade.php

@ -0,0 +1,58 @@
@extends('layouts.app')
@section('title', trans('project.files') . ' | ' . $project->name)
@section('content')
@include('projects.partials.breadcrumb',['title' => trans('project.files')])
<h1 class="page-header">
{{ $project->name }} <small>{{ trans('project.files') }}</small>
</h1>
@include('projects.partials.nav-tabs')
<div class="row">
<div class="col-md-8">
<div class="panel panel-default table-responsive">
<div class="panel-heading">
<h3 class="panel-title">{{ trans('project.files') }}</h3>
</div>
<table class="table table-condensed table-striped">
<thead>
<th>{{ trans('app.table_no') }}</th>
<th>{{ trans('file.title') }}</th>
<th class="text-center">{{ trans('file.description') }}</th>
<th class="text-center">{{ trans('app.action') }}</th>
</thead>
<tbody class="sort-files">
@forelse($files as $key => $file)
<tr id="{{ $file->id }}">
<td>{{ 1 + $key }}</td>
<td>{{ $file->title }}</td>
<td>{{ $file->description }}</td>
<td class="text-center"></td>
</tr>
@empty
<tr><td colspan="4">{{ trans('file.empty') }}</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">Upload new File</h3></div>
<div class="panel-body">
{!! Form::open(['route' => ['files.upload', $project->id], 'id' => 'upload-files', 'files' => true]) !!}
{{ Form::hidden('fileable_type', get_class($project)) }}
{!! FormField::file('files', ['label' => 'Upload File', 'placeholder' => 'Pilih File']) !!}
{!! FormField::text('title') !!}
{!! FormField::textarea('description') !!}
{!! Form::submit(trans('app.upload_files'), ['class' => 'btn btn-info']) !!}
{!! Form::close() !!}
</div>
</div>
</div>
</div>
@endsection

3
resources/views/projects/partials/nav-tabs.blade.php

@ -12,5 +12,8 @@
<li class="{{ Request::segment(3) == 'subscriptions' ? 'active' : '' }}"> <li class="{{ Request::segment(3) == 'subscriptions' ? 'active' : '' }}">
{!! link_to_route('projects.subscriptions', trans('project.subscriptions'), [$project->id]) !!} {!! link_to_route('projects.subscriptions', trans('project.subscriptions'), [$project->id]) !!}
</li> </li>
<li class="{{ Request::segment(3) == 'files' ? 'active' : '' }}">
{!! link_to_route('projects.files', trans('project.files'), [$project->id]) !!}
</li>
</ul> </ul>
<br> <br>

7
routes/web/projects.php

@ -16,7 +16,6 @@ Route::group(['middleware' => ['web','role:admin'], 'namespace' => 'Projects'],
/** /**
* Features Routes * Features Routes
*/ */
Route::get('projects/{id}/features/create', ['as'=>'features.create', 'uses'=>'FeaturesController@create']); Route::get('projects/{id}/features/create', ['as'=>'features.create', 'uses'=>'FeaturesController@create']);
Route::get('projects/{id}/features/add-from-other-project', ['as'=>'features.add-from-other-project', 'uses'=>'FeaturesController@addFromOtherProject']); Route::get('projects/{id}/features/add-from-other-project', ['as'=>'features.add-from-other-project', 'uses'=>'FeaturesController@addFromOtherProject']);
Route::post('features/{id}/tasks-reorder', ['as'=>'features.tasks-reorder', 'uses'=>'FeaturesController@tasksReorder']); Route::post('features/{id}/tasks-reorder', ['as'=>'features.tasks-reorder', 'uses'=>'FeaturesController@tasksReorder']);
@ -32,4 +31,10 @@ Route::group(['middleware' => ['web','role:admin'], 'namespace' => 'Projects'],
Route::post('features/{id}/tasks', ['as'=>'tasks.store', 'uses'=>'TasksController@store']); Route::post('features/{id}/tasks', ['as'=>'tasks.store', 'uses'=>'TasksController@store']);
Route::patch('task/{id}', ['as'=>'tasks.update', 'uses'=>'TasksController@update']); Route::patch('task/{id}', ['as'=>'tasks.update', 'uses'=>'TasksController@update']);
Route::delete('task/{id}', ['as'=>'tasks.destroy', 'uses'=>'TasksController@destroy']); Route::delete('task/{id}', ['as'=>'tasks.destroy', 'uses'=>'TasksController@destroy']);
/**
* Files Routes
*/
Route::get('projects/{project}/files', ['as' => 'projects.files', 'uses' => 'FilesController@index']);
Route::post('files/{fileable}', ['as' => 'files.upload', 'uses' => 'FilesController@create']);
}); });

38
tests/Feature/Projects/UploadFilesTest.php

@ -0,0 +1,38 @@
<?php
namespace Tests\Feature\Projects;
use App\Entities\Projects\Project;
use Tests\TestCase;
class UploadFilesTest extends TestCase
{
/** @test */
public function user_can_upload_document_to_a_project()
{
$user = $this->adminUserSigningIn();
$project = factory(Project::class)->create(['owner_id' => $user->id]);
$this->visit(route('projects.files', $project->id));
$this->seeElement('form', ['id' => 'upload-files']);
$this->seeElement('input', ['id' => 'files']);
$this->seeElement('input', ['type' => 'submit', 'value' => trans('app.upload_files')]);
$this->attach(storage_path('app/guitar-640.jpg'), 'files');
$this->type('Judul file', 'title');
$this->type('Deskripsi file yang diuplod.', 'description');
$this->press(trans('app.upload_files'));
$this->assertCount(1, $project->files);
$this->seeInDatabase('files', [
'fileable_id' => $project->id,
'fileable_type' => 'App\Entities\Projects\Project',
'title' => 'Judul file',
'description' => 'Deskripsi file yang diuplod.',
]);
$file = $project->files->first();
$filePath = storage_path('app/public/files/' . $file->filename);
$this->assertFileExistsThenDelete($filePath, 'File doesn\'t exists.');
}
}

8
tests/TestCase.php

@ -51,4 +51,12 @@ class TestCase extends \Laravel\BrowserKitTesting\TestCase
return $user; return $user;
} }
protected function assertFileExistsThenDelete($filePath)
{
$this->assertTrue(file_exists($filePath));
unlink($filePath);
$this->assertFalse(file_exists($filePath));
}
} }

8
tests/Unit/Models/ProjectTest.php

@ -4,6 +4,7 @@ namespace Tests\Unit\Models;
use App\Entities\Payments\Payment; use App\Entities\Payments\Payment;
use App\Entities\Projects\Feature; use App\Entities\Projects\Feature;
use App\Entities\Projects\File;
use App\Entities\Projects\Project; use App\Entities\Projects\Project;
use App\Entities\Projects\Task; use App\Entities\Projects\Task;
use App\Entities\Subscriptions\Subscription; use App\Entities\Subscriptions\Subscription;
@ -124,4 +125,11 @@ class ProjectTest extends TestCase
$this->assertEquals(0, $project->getFeatureOveralProgress()); $this->assertEquals(0, $project->getFeatureOveralProgress());
} }
/** @test */
public function it_has_many_files()
{
$project = factory(Project::class)->create();
$this->assertTrue($project->files instanceOf Collection);
}
} }
Loading…
Cancel
Save