How to handle multiple file upload in Laravel - laravel

In my Vue component, I'm sending an Axios request with an array of multiple files. How can I handle the individual files in my Laravel controller?
I always have an error saying 'Invalid argument supplied for foreach()'.
My Vue method to send the files:
uploadFiles() {
this.isPending = true;
this.error = null;
let f = new FormData();
f.append('files', this.files);
axios.post(`items/${this.$route.params.productId}/upload_files`, f)
.then((res) => {
console.log(res);
this.isPending = false;
}).catch((err) => {
console.log(err);
this.isPending = false;
this.error = 'Er is iets misgegaan. Probeer het nog een keer.';
});
}
My controller function:
public function upload_files(Request $request, $id)
{
$request->validate([
'files' => 'required'
]);
foreach ($request['files'] as $file) {
Storage::put($file->getClientOriginalName(), file_get_contents($file));
$path = $request->file('files')->store('public');
return $path;
}
}

Related

how can I customize json responses of laravel api and show them in vuex

Trying different solutions, I am fooling around with
response()->json([ ])
To create responses that I can read in my vue / vuex application
The Laravel api function that stores a new Speler ( dutch for player ;)):
I have trouble sending the created, or found Speler-object, through the response to the vuex-store.
Tried to set the status to 202 when succesfully logged, yet the actual status sent is 200..
It is clear that I do not understand it well enough. Can anyone help and explain?
public function store(Request $request)
{
if (Game::where('id',$request['game_id'])->exists() ){
if (!Speler::where('name',$request['name'])->where('game_id',$request['game_id'])->exists()){
$newSpeler = Speler::create(
[
'name' => $request['name'],
'pass_code' => $request['pass_code'],
'game_id' => $request['game_id']
])->first());
return $newSpeler;
}
elseif ( Speler::where('name',$request['name'])->where('game_id',$request['game_id'])->where('pass_code', $request['pass_code'])->exists()){
$speler = Speler::where('name',$request['name'])->where('game_id',$request['game_id'])->where('pass_code', $request['pass_code']);
return response()->json(['speler'=> $speler, 202]);
}
return response()->json(['status' => 'This name is already used, pass-code is not correct', 409]);
}
return response()->json([ 'status' => 'The game-pin does not exist', 403 ]);
}
This is called form the vuex actions:
export const addSpeler = ({commit}, formData) => {
return new Promise((resolve, reject) => {
fetch(`api/speler`, {
method: 'post',
body:formData,
})
.then(res => {
if (res.status === 202){
resolve('De speler is succesfully logged on');
commit('SET_CURRENT_SPELER', res.data.speler);
}
else if (res.status === 201){
commit('SET_CURRENT_SPELER', res.data);
resolve('De speler is succesfully added')
}
else {
reject('De speler is not logged in. Name exists and does not match passcode');
}
})
.catch(err => {
reject(err.message)
});
})
}
and this is called from a vue method:
methods: {
addSpeler(){
this.errorMessage ='';
this.spelerAdded =false;
const formData = new FormData();
formData.append('name', this.name);
formData.append('pass_code',this.pass_code);
formData.append('game_id', this.currentGame.id);
this.$store.dispatch('addSpeler', formData )
.then(res => {
this.spelerAdded = true;
console.log(res.status);
})
.catch(err => {
this.errorMessage = err;
this.spelerAdded = false;
});
},
mutations.js:
export const SET_CURRENT_SPELER = (state, speler) => {
state.currentSpeler = speler;
}
state.js:
export default{
currentGame:{},
currentSpeler:{}
}
The comment by porloscerros answered the question perfectly :
the status goes as the second argument of the json method return response()->json(['speler'=> $speler], 202); (and not inside the array as you are doing). If you don't pass a second argument, the argument value is assigned to 200 by default json(mixed $data = [], int $status = 200, array $headers = [], int $options = 0)

How to avoid error "The PUT method is not supported for this route. Supported methods: GET, HEAD." using Laravel

I am using Laravel 7 and Vue.js 2.
When I edit a room I should update the rooms table and then to redirect to the admin page with a succesfull message.
Unfortunately when I submit the form I edit correctly the table but then the redirect fails. It appears the following error message:
message: "The PUT method is not supported for this route. Supported methods: GET, HEAD.
This is my two methods in AdminController:
public function index()
{
$permissions = Permission::select('id')->get();
$rooms = Room::all();
$languages = Language::all();
$users = UserResource::collection(User::all());
return view('admin')->with(['success_message' => '', 'permissions'=>$permissions, 'users'=>$users, 'rooms'=>$rooms, 'languages'=>$languages]);
}
public function edit_room (Request $request) {
$validator = Validator::make($request->all(), [
'id' => 'required',
'name' => 'required'
]);
if ($validator->fails()) {
return response($validator->errors());
}
$room = Room::find($request->id);
$room->name = $request->name;
$room->save();
$success_message = "The room " . $request->name . " has been correctly edited";
return Redirect::route('admin')->with( ['success_message' => $success_message] );
}
This is the axios call in my child component:
editRoom: function() {
axios.put('edit_room', { id: this.rooms[this.index].id, name: this.roomName })
.then((response) => {
console.log(response);
this.errors = response.data;
if (Object.keys(this.errors).length === 0) {
alert('viva');
this.user = {};
} else {
alert('noviva');
}
})
.catch(error => {
alert(noooooo);
console.log(error);
});
}
This is my two routes in web.php:
Route::put('edit_room', 'AdminController#edit_room')->name('edit_room');
Route::get('/admin', 'AdminController#index')->name('admin');
This is an update of the table so I suppose I should use a PUT method but for some reason it doesn't.
Which is the best way to solve this error?
I think the problem is that you send your request via XHR
So when you using
return Redirect::route('admin')->with( ['success_message' => $success_message]
it sends an response with 301 http code to redirect your browser
i think you should refactor your code like this for example
return 'success_message'
and then in your axios after console.log(response);
window.location.href = "http://www.your_address.com/admin";

Missing param for for named route after successful api call

I have a Settings component where a user can update their informations, after the update is successful I get in the console missing param for named route and all the inputs that should contain user data are empty, and when I try accessing the settings page it redirects to the home page even though all the data needed for it to work is still in the component's data.
This is the component's method to that commits an action in vuex store
updateUser(){
const fd = new FormData()
//check if avatar is of type file
if ('File' in window && this.avatar instanceof File){
fd.append('avatar', this.avatar, this.avatar.name)
}
fd.append('_method', 'PUT')
fd.append('city_id', this.userData? this.userData.city.id:this.city)
fd.append('oldPassword', this.oldPassword)
fd.append('newPassword', this.newPassword)
this.$store.dispatch('User/update', fd)
.then(() =>{
this.success = this.user.user.message
}).catch(err => {
this.error = err.response.data.message
})
}
the above method triggers this vuex action
update({commit}, user){
return axios.post(`/api/${user.name}/update`, user)
.then(( {data} ) => {
commit('UPDATE_USER_DATA', data)
})
}
And here's the UPDATE_USER_DATA mutation
UPDATE_USER_DATA(state, userData){
let user = localStorage.getItem('user')
const data = JSON.parse(user)
state.user.user = userData
data['user'] = userData
localStorage.setItem('user', JSON.stringify(data))
}
This is the laravel method that's called
public function update(User $user, Request $request)
{
$attributes = [];
if(request('city_id') && $request['city_id'] !== null){
$attributes['city_id'] = $request['city_id'];
}
if (request('oldPassword') && Hash::check($request['oldPassword'], auth()->user()->getAuthPassword())
){
if(request('newPassword')) {
$attributes['password'] = Hash::make($request['newPassword']);
}
} else{
return response()->json([
'message' => "L'ancien mot de passe est incorrecte.",
'user' => $user
], 400);
}
if (request('avatar')) {
$attributes['avatar'] = request('avatar')->store('avatars');
}
// This is where I have the issue
if (!empty($attributes)){
if($user->update($attributes)){
return response()->json([
'user' => $user,
'message' => 'Votre compte a été modifié avec succés'
], 200);
}
}
return response()->json([
'message' => "Votre compte n'a pas été modifié, réessayez plus tard",
'user' => $user
], 400);
}

Form Data file return null in laravel/lumen when using axios from react native

It's my first time using React Native and Laravel.
I am trying to create an upload form in React Native to Lumen API.
When I try to upload file to Lumen using Postman, it works fine. But when using Axios in React Native:
$request->file('file')
returns
null
public function store(Request $request) {
$this->validate($request, [
"file" => 'required|file',
"from" => 'required',
"to" => 'required',
"subject" => 'required',
"doc_date" => 'required',
"doc_type" => 'required',
"doc_number" => 'required',
]);
$file = $request->file('file');
$fileName = $file->getClientOriginalName();
$saved_name = uniqid() . '_' . $fileName;
$path = "uploads" . DIRECTORY_SEPARATOR . "documents" . DIRECTORY_SEPARATOR;
$file->move($path, $saved_name);
$document = new Document();
$document->from = $request->input("from");
$document->to = $request->input("to");
$document->subject = $request->input("subject");
$document->doc_date = $request->input("doc_date");
$document->doc_type = $request->input("doc_type");
$document->doc_number = $request->input("doc_number");
$document->file_path = $path . $saved_name;
$document->save();
return response()->json(['document' => $document], 201);
}
## Client
async uploadDocument(document, file) {
// document is some form data
// document: {
// from: 'me#mail.com',
// to: 'someone#gmail.com',
// doc_date: '2020-02-01',
// ....
// }
// file from react-native-document-picker
// file: {
// fileName: 'blablabla.pdf',
// type: 'blablabla/blabla',
// uri: 'content://blablabla',
// }
let resp;
try {
resp = await axios.post(
`${API_ENDPOINT}/documents`,
helper.createFormData(file, document),
{
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
},
);
} catch (e) {
console.log(`Axios error: ${e.message}`);
throw e;
}
console.log(`Server response: ${JSON.stringify(resp.data)}`);
return resp;
},
## Helper
export default {
createFormData(file, body) {
const data = new FormData();
data.append('file', {
name: file.fileName,
type: file.type,
uri: file.uri,
});
Object.keys(body).forEach(key => {
data.append(key, body[key]);
});
return data;
},
};

laravel on saving model return json from validation

Hi I'm having a problem outputting my json information on saving method in the model. I get the following error -
UnexpectedValueException in Response.php line 397:
The Response content must be a string or object implementing __toString(), "boolean" given.
I do validation on the model while saving and in the validate method of the model I need to out put the json but I'm getting boolean instead of json object
Javascript:
submit: function(e) {
e.preventDefault();
var contact = this.model.save({
firstname: this.firstname.val(),
lastname: this.lastname.val(),
company: this.company.val(),
email_address: this.email_address.val(),
description: this.description.val(),
}, {success:function(response){ console.log(response)}, wait: true});
Contact Model:
class Contact extends Model
{
protected $table = "contacts";
protected $fillable = ['firstname', 'lastname', 'company', 'email_address', 'description'];
public static function boot() {
parent::boot();
static::creating(function($model) {
return $model->validate('POST');
});
static::updating(function($model) {
return $model->validate('PUT');
});
static::saving(function($model) {
return $model->validate('PUT');
});
}
public function rules($method)
{
switch($method)
{
case 'GET':
case 'DELETE':
{
return [];
}
case 'POST':
{
return [
'firstname' => 'required',
'lastname' => 'required',
'email_address' => 'required|email|unique:contacts,email_address',
'description' => 'requried'
];
}
case 'PUT':
case 'PATCH':
{
return [
'firstname' => 'required',
'lastname' => 'required',
'email_address' => 'required|email|unique:contacts,email_address,'.$this->id,
'description' => 'required',
];
}
default: break;
}
return [];
}
public function messages() {
return [
'firstname.required' => 'Please enter your first name.',
'lastname.required' => 'Please enter your first name.',
'email_address.required' => 'Please enter a email address.',
'email_address.email' => 'Please enter a valid email address',
'email_address.unique' => 'The email is not unique.',
'description' => 'Please enter a description.'
];
}
public function validate($method)
{
$data = $this->attributes;
// if( $data['slug'] === '') {
// // if the slug is blank, create one from title data
// $data['slug'] = str_slug( $data['title'], '-' );
// }
// make a new validator object
$v = Validator::make($data, $this->rules($method), $this->messages());
// check for failure
if ($v->fails())
{
// set errors and return false
// json here not return response it's always boolean true or false
return new JsonResponse(array('error' => true, 'errors' => $v->messages()));
}
// validation pass
return true; //new JsonResponse(array('errors'=>false));
}
public function errors() {
return $this->errors;
}
public function user() {
return $this->hasOne('App\User', 'email', 'email_address');
}
}
Saving the model:
public function update(Request $request, $id) {
$contact = Contact::find($id)->with('user')->first();
$contact->firstname = $request->get('firstname');
$contact->lastname = $request->get('lastname');
$contact->email_address = $request->get('email_address');
$contact->company = $request->get('company');
$contact->description = $request->get('description');
return $contact->save(); //return formatted json
}
According to your implementation of validation, you should change the following part (in Contact):
// check for failure
if ($v->fails())
{
// set errors and return false
// json here not return response it's always boolean true or false
return new JsonResponse(array('error' => true, 'errors' => $v->messages()));
}
To something like this:
if ($v->fails()) {
$this->errors = $v->errors();
return false;
}
Then, from the Controller, try something like this:
// If validation failed
if(!$contact->save()) {
return response()->json([
'error' => true,
'errors' => $contact->errors()
]);
}
// Contact created if reached here...
return response()->json(['error' => false, 'contact' => $contact]);
Also, check the Ajax-Request-Validation and Form-Request-Validation (Easier and Managable).
Note: Don't try to return any kind of HTTP Response from model. Returning the HTTP response is part of your application logic and model should not care about these.
As save() does return boolean so You've to check if it's ok.
1) Change Your Contact model to put errors to model's errors param:
/* if($v->fails()) remove/comment this line
...
} */
$this->errors = $v->errors();
return !$v->fails();
2) In Your controller put this code:
public function update(Request $request, $id) {
$contact = Contact::find($id)->with('user')->first();
if(!$contact) {
return response('Contact not found', 404);
}
$contact->firstname = $request->get('firstname');
$contact->lastname = $request->get('lastname');
$contact->email_address = $request->get('email_address');
$contact->company = $request->get('company');
$contact->description = $request->get('description');
return $contact->save()?
$contact->toJson() : // returns 200 OK status with contact (json)
response($contact->errors, 400); // returns proper 400 Bad Request header with errors (json) in it
}
p.s. it's nice to answer to requester with http status, industry has made all to make life of developer easy, so if it's not 2xx, 3xx status so => response for Your request from client-side app will be treated as error (handler success: function(response) will not catch error here)

Resources