I'm trying to create a page where a user can add a title and then upload an image or 2. I'm using dropzone and Laravel and I've tried to make it look different, I made it look like http://www.dropzonejs.com/bootstrap.html
The problem I'm having is that I need to add a url to the js file but it keeps giving me this error
POST http://crud.test/portfolios 419 (unknown status)
and in my Preview in my dev tools
{message: "", exception: "Symfony\Component\HttpKernel\Exception\HttpException",…}
I know that in Laravel I would use
{{ csrf_field() }}
and I can't upload my images to the folder I specified in my controller or save the images to my database.
What I would like is how do I get my images to upload to the folder and save to my database.
My blade:
<form action="{{ route('portfolios.store') }}">
{{ csrf_field() }}
<div class="form-group">
<label>Title</label>
<input type="title" name="title" class="form-control">
</div>
<div id="actions" class="row">
<div class="col-lg-7">
<span class="btn btn-success fileinput-button dz-clickable">
<i class="glyphicon glyphicon-plus"></i>
<span>Add files...</span>
</span>
<button type="button" class="btn btn-info start">
<i class="glyphicon glyphicon-upload"></i>
<span>Start upload</span>
</button>
<button type="reset" class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel upload</span>
</button>
</div>
<div class="col-lg-5">
<span class="fileupload-process">
<div id="total-progress" class="progress progress-striped active total-upload-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress=""></div>
</div>
</span>
</div>
</div>
<div class="table table-striped files" id="previews">
<div id="template" class="file-row dz-image-preview">
<div>
<span class="preview">
<img data-dz-thumbnail />
</span>
</div>
<div>
<p class="name" data-dz-name></p>
<strong class="error text-danger" data-dz-errormessage></strong>
</div>
<div>
<p class="size" data-dz-size></p>
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress></div>
</div>
</div>
<div>
<button class="btn btn-info start">
<i class="glyphicon glyphicon-upload"></i>
<span>Start</span>
</button>
<button data-dz-remove class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel</span>
</button>
<button data-dz-remove class="btn btn-danger delete">
<i class="glyphicon glyphicon-trash"></i>
<span>Delete</span>
</button>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
My main.js:
var previewNode = document.querySelector("#template");
previewNode.id = "";
var previewTemplate = previewNode.parentNode.innerHTML;
previewNode.parentNode.removeChild(previewNode);
var myDropzone = new Dropzone(document.body, { // Make the whole body a dropzone
url: "/portfolios", // Set the url
thumbnailWidth: 80,
thumbnailHeight: 80,
parallelUploads: 20,
previewTemplate: previewTemplate,
autoQueue: false, // Make sure the files aren't queued until manually added
uploadMultiple: true,
previewsContainer: "#previews", // Define the container to display the previews
clickable: ".fileinput-button" // Define the element that should be used as click trigger to select files.
});
myDropzone.on("addedfile", function(file) {
file.previewElement.querySelector(".start").onclick = function() {
myDropzone.enqueueFile(file);
};
});
myDropzone.on("totaluploadprogress", function(progress) {
document.querySelector("#total-progress .progress-bar").style.width = progress + "%";
});
myDropzone.on("sending", function(file) {
document.querySelector("#total-progress").style.opacity = "1";
file.previewElement.querySelector(".start").setAttribute("disabled", "disabled");
});
myDropzone.on("queuecomplete", function(progress) {
document.querySelector("#total-progress").style.opacity = "0";
});
document.querySelector("#actions .start").onclick = function() {
myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
};
document.querySelector("#actions .cancel").onclick = function() {
myDropzone.removeAllFiles(true);
};
My controller:
public function store(Request $request)
{
$portfolio = new Portfolio();
$portfolio->fill($this->getSafeInput($request));
if($request->hasFile('file'))
{
$names = [];
foreach($request->file('file') as $image)
{
$destinationPath = 'portfolio_images/';
$filename = $image->getClientOriginalName();
$image->move($destinationPath, $filename);
array_push($names, $filename);
}
$portfolio->file = json_encode($names);
}
$portfolio->save();
return redirect()->route('portfolios.index');
}
My routes:
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
Route::resource('/portfolios', 'PortfolioController');
I hope I've explained properly and if I've left any information out please let me know
The problem is that Dropzone will automatically POST your files when you drop them. But it does not include any other fields in the request it POSTs - just the file. So your CSRF token is missing (as are all other fields on your form), and Laravel throws an error.
There are a few ways around that. Perhaps simplest here is to add the CSRF token to the data POSTed in your sending()
even handler. The Dropzone docs even mention doing exactly this:
... you can modify them (for example to add a CSRF token) ...
So, in your code:
// First, include the 2nd and 3rd parameters passed to this handler
myDropzone.on("sending", function(file, xhr, formData) {
// Now, find your CSRF token
var token = $("input[name='_token']").val();
// Append the token to the formData Dropzone is going to POST
formData.append('_token', token);
// Rest of your code ...
document.querySelector("#total-progress").style.opacity = "1";
file.previewElement.querySelector(".start").setAttribute("disabled", "disabled");
});
Note though that your CSRF token, which is still on the form unchanged, is now invalid - the POST request just handled means it will have updated. The next file you drop will re-use the same token and it will fail.
A more complete solution is to disable sending of files completely until you hit your submit button, then send all files and all form fields at once. To do that, you must disable autoProcessQueue
:
autoProcessQueue = false,
Next, since the queue is not autoprocessed, you must now manually process it, by firing processQueue()
, when you submit your form. You need some kind of event handler to fire when your form is submitted:
// Event handler to fire when your form is submitted
$('form').on('submit', function(e) {
// Don't let the standard form submit, we're doing it here
e.preventDefault();
// Process the file queue
myDropzone.processQueue();
});
And finally, you need to add your CSRF token and any other fields in your form to the data Dropzone will POST:
myDropzone.on("sending", function(file, xhr, formData) {
// Find all input values on your form
var data = $('form').serializeArray();
// Append them all to the formData Dropzone will POST
$.each(data, function(key, el) {
formData.append(el.name, el.value);
});
// Rest of your code ...
document.querySelector("#total-progress").style.opacity = "1";
file.previewElement.querySelector(".start").setAttribute("disabled", "disabled");
});