Created
November 13, 2017 09:11
-
-
Save robertnicjoo/3dd1bbd1ccb9d3a7c0c36320d7e04d65 to your computer and use it in GitHub Desktop.
Laravel with Dropzone.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App; | |
use Illuminate\Database\Eloquent\Model; | |
class Attachment extends Model | |
{ | |
protected $guarded = []; | |
protected $appends = ['url']; | |
public function attachable() | |
{ | |
return $this->morphTo(); | |
} | |
public function getUrlAttribute() | |
{ | |
return Storage::url($this->uid); | |
} | |
public static function boot() | |
{ | |
parent::boot(); | |
static::deleting(function($attachment) { | |
// delete associated file from storage | |
Storage::disk('public')->delete($attachment->uid); | |
}); | |
} | |
return [ | |
// Allowed file types with . prefix | |
'allowed' => '.pdf,.doc,.xls,.docx,.xlsx,.jpg,.png,.gif,.jpeg', | |
// Max file size in KB | |
'max_size' => 5000 | |
]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Controllers; | |
use Illuminate\Http\Request; | |
class AttachmentController extends Controller | |
{ | |
public function store(Request $request) | |
{ | |
$request->validate([ | |
'file' => 'required|file|max:5000|mimes:' . $this->getAllowedFileTypes(), | |
'attachable_id' => 'required|integer', | |
'attachable_type' => 'required', | |
]); | |
// save the file | |
if ($fileUid = $request->file->store('/upload', 'public')) { | |
return Attachment::create([ | |
'filename' => $request->file->getClientOriginalName(), | |
'uid' => $fileUid, | |
'size' => $request->file->getClientSize(), | |
'mime' => $request->file->getMimeType(), | |
'attachable_id' => $request->get('attachable_id'), | |
'attachable_type' => $request->get('attachable_type'), | |
]); | |
} | |
return response(['msg' => 'Unable to upload your file.'], 400); | |
} | |
/** | |
* Remove the specified resource from storage. | |
* | |
* @param \App\Attachment $attachment | |
* @return \Illuminate\Http\Response | |
*/ | |
public function destroy(Attachment $attachment) | |
{ | |
return (string) $attachment->delete(); | |
} | |
/** | |
* Remove . prefix so laravel validator can use allowed files | |
* | |
* @return string | |
*/ | |
private function getAllowedFileTypes() | |
{ | |
return str_replace('.', '', config('attachment.allowed', '')); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@extends('layouts.app') | |
@section('title', 'Add New Product') | |
@section('styles') | |
<script src="{{asset('js/tinymce/jquery.tinymce.min.js')}}"></script> | |
<script src="{{asset('js/tinymce/tinymce.min.js')}}"></script> | |
<link href="{{asset('css/dropzone.css')}}" rel='stylesheet' type='text/css' /> | |
<script type="text/javascript" src="{{asset('js/dropzone.js')}}"></script> | |
@endsection | |
@section('content') | |
@hasrole('Admin') | |
Your Role: {{ Auth::user()->roles()->pluck('name')->implode(' ') }} | |
<div class="panel panel-default"> | |
<div class="panel-heading">Products</div> | |
<div class="panel-body"> | |
@component('admin.uploader', [ | |
'title' => 'Upload Post Images', | |
'params' => [ | |
'attachable_id' => 1, | |
'attachable_type' => 'App\Product' | |
], | |
'acceptedFiles' => '.jpg,.png', | |
]) | |
@endcomponent | |
<br> | |
@component('admin.uploader', [ | |
'title' => 'Document Uploader', | |
'desc' => 'Upload PDF, DOC, or XLS document', | |
'params' => [ | |
'attachable_id' => 2, | |
'attachable_type' => 'App\Product' | |
], | |
'acceptedFiles' => '.doc,.xls,.pdf, .docx', | |
]) | |
@endcomponent | |
<form action="{{route('products.store')}}" method="POST" enctype="multipart/form-data"> | |
{{ csrf_field() }} | |
<div class="row"> | |
<div class="col-md-8"> | |
<label for="title">Title</label> | |
<input type="text" value="{{old('title')}}" name="title" class="mb-20 form-control"> | |
<div class="row"> | |
<div class="col-md-6"> | |
<label for="slug">Slug</label> | |
<input type="text" value="{{old('slug')}}" name="slug" class="mb-20 form-control"> | |
</div> | |
<div class="col-md-6"> | |
<label for="sku">SKU</label> | |
<input type="text" value="{{old('sku')}}" name="sku" class="mb-20 form-control"> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-md-6 mt-20"> | |
<label for="image_one">Image 1</label> | |
<input type="file" class="form-control" name="image_one" id="image"> | |
</div> | |
<div class="col-md-6 mt-20"> | |
<label for="image_two">Image 2</label> | |
<input type="file" class="form-control" name="image_two" id="image"> | |
</div> | |
<div class="col-md-6 mt-20"> | |
<label for="image_three">Image 3</label> | |
<input type="file" class="form-control" name="image_three" id="image"> | |
</div> | |
<div class="col-md-6 mt-20"> | |
<label for="image_four">Image 4</label> | |
<input type="file" class="form-control" name="image_four" id="image"> | |
</div> | |
<div class="col-md-6 mt-20"> | |
<label for="user_id">Author</label> | |
<input type="text" name="user_id" class="mb-20 form-control" value="{{$user->id}}" placeholder="{{$user->username}}"> | |
</div> | |
</div> | |
<div class="mt-20"> | |
<label for="short_description">Short Description</label> | |
<textarea value="{{old('short_description')}}" class="form-control mb-20 editor" name="short_description" rows="5" cols="50"></textarea> | |
</div> | |
<div class="mt-20"> | |
<label for="description">Description</label> | |
<textarea value="{{old('description')}}" class="form-control mb-20 editor" name="description" rows="10" cols="50"></textarea> | |
</div> | |
</div><!--end col-md-8--> | |
<div class="col-md-4"> | |
<label for="arrivialin">Arrivial In</label> | |
<input type="text" placeholder="days number (ONLY) 'eg. 5'" value="{{old('arrivialin')}}" name="arrivialin" class="mb-20 form-control"> | |
<label for="brand_id">Brand</label> | |
<select class="form-control" name="brand_id"> | |
<option>Select Brand</option> | |
@foreach($brands as $brand) | |
<option value="{{ $brand->id }}">{{ $brand->title }}</option> | |
@endforeach | |
</select> | |
<!-- end brand --> | |
<label for="subcategory_id">Category</label> | |
<select class="form-control" name="subcategory_id"> | |
<option>Select Category</option> | |
@foreach($categories as $category) | |
<optgroup label="{{ $category->title }}"><!--have isssue --> | |
@foreach($category->subcategories as $sub) | |
<option value="{{ $sub->id }}">{{ $sub->title }}</option> | |
@endforeach | |
</optgroup><!--have isssue --> | |
@endforeach | |
</select> | |
<!-- end category --> | |
<label for="submores">Attributes</label> | |
<select class="tagsselector form-control" name="submores[]" multiple="multiple"> | |
<option>Select Attribute</option> | |
@foreach($submores as $more) | |
<option value="{{ $more->id }}">{{ $more->title }}</option> | |
@endforeach | |
</select> | |
<!-- end category --> | |
<label for="submores">Options</label> | |
<select class="tagsselector form-control" name="suboptions[]" multiple="multiple"> | |
<option>Select Option</option> | |
@foreach($suboptions as $option) | |
<option value="{{ $option->id }}">{{ $option->title }} - {{$option->price}} Rp</option> | |
@endforeach | |
</select> | |
<!-- end category --> | |
<label for="price">price</label> | |
<input type="text" value="{{old('price')}}" name="price" class="mb-20 form-control"> | |
<!-- end price --> | |
<label for="stock">Stock</label> | |
<input type="text" value="{{old('stock')}}" name="stock" class="mb-20 form-control"> | |
<!-- end price --> | |
<label for="status_id">Status</label> | |
<select class="form-control" name="status_id"> | |
<option>Select Status</option> | |
@foreach($statuses as $status) | |
<option value="{{ $status->id }}">{{ $status->title }}</option> | |
@endforeach | |
</select> | |
<!-- end status --> | |
<h3 class="mt-20">Product Sizes</h3> | |
<div class="row"> | |
<div class="col-md-6"> | |
<label for="weight">weight</label> | |
<input type="weight" placeholder="kg" value="{{old('weight')}}" name="weight" class="mb-20 form-control"> | |
</div> | |
<div class="col-md-6"> | |
<label for="length">length</label> | |
<input type="length" placeholder="mm" value="{{old('length')}}" name="length" class="mb-20 form-control"> | |
</div> | |
<div class="col-md-6"> | |
<label for="height">height</label> | |
<input type="height" placeholder="mm" value="{{old('height')}}" name="height" class="mb-20 form-control"> | |
</div> | |
<div class="col-md-6"> | |
<label for="width">width</label> | |
<input type="width" placeholder="mm" value="{{old('width')}}" name="width" class="mb-20 form-control"> | |
</div> | |
</div> | |
<button type="submit" class="btn btn-block btn-success" name="button">Publish</button> | |
</div><!--end col-md-4--> | |
</div><!--end row--> | |
</form> | |
</div><!--end panel-body--> | |
</div> | |
@endhasrole | |
@endsection | |
@section('scripts') | |
<link href="{{asset('default/admin/css/select2.min.css')}}" rel='stylesheet' type='text/css' /> | |
<script type="text/javascript" src="{{asset('default/admin/js/select2.min.js')}}"></script> | |
<script type="text/javascript"> | |
$(".tagsselector").select2(); | |
</script> | |
<script> | |
var editor_config = { | |
path_absolute : "/", | |
selector: "textarea.editor", | |
plugins: [ | |
"advlist autolink lists link image charmap print preview hr anchor pagebreak", | |
"searchreplace wordcount visualblocks visualchars code fullscreen", | |
"insertdatetime media nonbreaking save table contextmenu directionality", | |
"emoticons template paste textcolor colorpicker textpattern codesample", | |
"fullpage toc tinymcespellchecker imagetools help" | |
], | |
toolbar: "insertfile undo redo | styleselect | bold italic strikethrough | alignleft aligncenter alignright alignjustify | ltr rtl | bullist numlist outdent indent removeformat formatselect| link image media | emoticons charmap | code codesample | forecolor backcolor", | |
external_plugins: { "nanospell": "http://sinarmulia.dev/js/tinymce/plugins/nanospell/plugin.js" }, | |
nanospell_server:"php", | |
browser_spellcheck: true, | |
relative_urls: false, | |
remove_script_host: false, | |
file_browser_callback : function(field_name, url, type, win) { | |
var x = window.innerWidth || document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth; | |
var y = window.innerHeight|| document.documentElement.clientHeight|| document.getElementsByTagName('body')[0].clientHeight; | |
var cmsURL = editor_config.path_absolute + 'laravel-filemanager?field_name=' + field_name; | |
if (type == 'image') { | |
cmsURL = cmsURL + "&type=Images"; | |
} else { | |
cmsURL = cmsURL + "&type=Files"; | |
} | |
tinymce.activeEditor.windowManager.open({ | |
file: '<?= route('elfinder.tinymce4') ?>',// use an absolute path! | |
title: 'Sinarmulia file manager', | |
width: 900, | |
height: 450, | |
resizable: 'yes' | |
}, { | |
setUrl: function (url) { | |
win.document.getElementById(field_name).value = url; | |
} | |
}); | |
} | |
}; | |
tinymce.init(editor_config); | |
</script> | |
<script> | |
{!! \File::get(base_path('vendor/barryvdh/laravel-elfinder/resources/assets/js/standalonepopup.min.js')) !!} | |
</script> | |
@endsection |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App; | |
use Illuminate\Database\Eloquent\Model; | |
use jpmurray\LaravelCountdown\Traits\CalculateTimeDiff; | |
use willvincent\Rateable\Rateable; | |
class Product extends Model | |
{ | |
//other functions..... | |
public function attachments() | |
{ | |
return $this->morphMany(Attachment::class, 'attachable'); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@php $dropzoneId = isset($dz_id) ? $dz_id : str_random(8); @endphp | |
<div id="{{$dropzoneId}}" class="dropzone"> | |
<div class="dz-default dz-message"> | |
<h3>{{ $title or 'Drop files here or click to upload.'}}</h3> | |
<p class="text-muted">{{ $desc or 'Any related files you can upload' }} <br> | |
<small>One file can be max {{ config('attachment.max_size', 0) / 1000 }} MB</small></p> | |
</div> | |
</div> | |
<!-- Dropzone {{ $dropzoneId }} --> | |
@push('scripts') | |
<script> | |
// Turn off auto discovery | |
Dropzone.autoDiscover = false; | |
$(function () { | |
// Attach dropzone on element | |
$("#{{ $dropzoneId }}").dropzone({ | |
url: "{{ route('attachments.store') }}", | |
addRemoveLinks: true, | |
maxFilesize: {{ isset($maxFileSize) ? $maxFileSize : config('attachment.max_size', 1000) / 1000 }}, | |
acceptedFiles: "{!! isset($acceptedFiles) ? $acceptedFiles : config('attachment.allowed') !!}", | |
headers: {'X-CSRF-TOKEN': "{{ csrf_token() }}"}, | |
params: {!! isset($params) ? json_encode($params) : '{}' !!}, | |
init: function () { | |
// uploaded files | |
var uploadedFiles = []; | |
@if(isset($uploadedFiles) && count($uploadedFiles)) | |
// show already uploaded files | |
uploadedFiles = {!! json_encode($uploadedFiles) !!}; | |
var self = this; | |
uploadedFiles.forEach(function (file) { | |
// Create a mock uploaded file: | |
var uploadedFile = { | |
name: file.filename, | |
size: file.size, | |
type: file.mime, | |
dataURL: file.url | |
}; | |
// Call the default addedfile event | |
self.emit("addedfile", uploadedFile); | |
// Image? lets make thumbnail | |
if( file.mime.indexOf('image') !== -1) { | |
self.createThumbnailFromUrl( | |
uploadedFile, | |
self.options.thumbnailWidth, | |
self.options.thumbnailHeight, | |
self.options.thumbnailMethod, | |
true, function(thumbnail) { | |
self.emit('thumbnail', uploadedFile, thumbnail); | |
}); | |
} else { | |
// we can get the icon for file type | |
self.emit("thumbnail", uploadedFile, getIconFromFilename(uploadedFile)); | |
} | |
// fire complete event to get rid of progress bar etc | |
self.emit("complete", uploadedFile); | |
}) | |
@endif | |
// Handle added file | |
this.on('addedfile', function(file) { | |
var thumb = getIconFromFilename(file); | |
$(file.previewElement).find(".dz-image img").attr("src", thumb); | |
}) | |
// handle remove file to delete on server | |
this.on("removedfile", function (file) { | |
// try to find in uploadedFiles | |
var found = uploadedFiles.find(function (item) { | |
// check if filename and size matched | |
return (item.filename === file.name) && (item.size === file.size); | |
}) | |
// If got the file lets make a delete request by id | |
if( found ) { | |
$.ajax({ | |
url: "/attachments/" + found.id, | |
type: 'DELETE', | |
headers: { | |
'X-CSRF-TOKEN': "{{ csrf_token() }}" | |
}, | |
success: function(response) { | |
console.log('deleted'); | |
} | |
}); | |
} | |
}); | |
// Handle errors | |
this.on('error', function(file, response) { | |
var errMsg = response; | |
if( response.message ) errMsg = response.message; | |
if( response.file ) errMsg = response.file[0]; | |
$(file.previewElement).find('.dz-error-message').text(errMsg); | |
}); | |
} | |
}); | |
}) | |
// Get Icon for file type | |
function getIconFromFilename(file) { | |
// get the extension | |
var ext = file.name.split('.').pop().toLowerCase(); | |
// if its not an image | |
if( file.type.indexOf('image') === -1 ) { | |
// handle the alias for extensions | |
if(ext === 'docx') { | |
ext = 'doc' | |
} else if (ext === 'xlsx') { | |
ext = 'xls' | |
} | |
return "/images/icon/"+ext+".svg"; | |
} | |
// return a placeholder for other files | |
return '/images/icon/txt.svg'; | |
} | |
</script> | |
@endpush |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment