Skip to content

Instantly share code, notes, and snippets.

@robertnicjoo
Created November 13, 2017 09:11
Show Gist options
  • Save robertnicjoo/3dd1bbd1ccb9d3a7c0c36320d7e04d65 to your computer and use it in GitHub Desktop.
Save robertnicjoo/3dd1bbd1ccb9d3a7c0c36320d7e04d65 to your computer and use it in GitHub Desktop.
Laravel with Dropzone.js
<?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
];
}
<?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', ''));
}
}
@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
<?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');
}
}
@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