how do I get the section title, sub_section_title and file in the formData in laravel - laravel

I am developing an application using laravel 8 and vuejs. I am trying to post form data from my vuejs to backend(laravel) but it is not working
The vuejs creates a subsection of a section which is add to an array of subsection inside the section array which is converted to string and added to a form data then sent as a request to my backend.
The frontend is working perfectly well but I cant access the data on my backend. How do I get the values of the course title, section title, sub section title and file added
Vuejs
<script>
import { reactive } from "vue";
import axios from "axios";
export default {
name: 'CreateCourse',
setup(){
const sections = reactive([{'section_title': '', 'sub_sections': [{'sub_section_title': '', 'file': '', 'url': ''}]}]);
const course = reactive({'title': '', 'description': ''});
const addSection = () => {
sections.push({"section_title": "", 'sub_sections': [{'sub_section_title': '', 'file': '', 'url': ''}]});
}
const addSubSection = (idx) => {
console.log('the value of idx is ', idx);
sections[idx].sub_sections.push({"sub_section_title": "", 'file': '', 'url': ''});
}
const uploadFile = (e, idx, i) => {
sections[idx].sub_sections[i].file = e.target.files[0];
sections[idx].sub_sections[i].url = URL.createObjectURL(sections[idx].sub_sections[i].file);
}
const createCourse = (e) => {
e.preventDefault();
let newCourse = JSON.stringify(course)
let newSection = JSON.stringify(sections)
const formData = new FormData();
formData.append("course", newCourse);
formData.append("sections", newSection);
showLoader(true);
axios.post('/api', form, { headers: {'Content-Type': 'multipart/form-data'}}).then(response =>
{
NotificationService.success(response.data.message);
showLoader(false);
course.title = '';
course.description = '';
}).catch(err => {
NotificationService.error(err.response);
showLoader(false);
});
}
return {
course,
createCourse,
sections,
addSection,
addSubSection,
uploadFile
}
}
</script>
laravel code
echo $request->get("title");
echo $request->get("description");
foreach($request->section_title as $titles)
{
echo $titles
}
foreach($request->section_sub_title as $sub_titles)
{
// info($sub_titles);
// return $sub_titles;
echo $sub_titles
}
{"course":{"title":"Frontend","description":"This is building web interface with html, css and javascript"},"sections":[{"section_title":"HTML","sub_sections":[{"sub_section_title":"What is HTML","file":{},"url":"blob:http://localhost:8080/ea0acc7d-34e6-4bff-9255-67794acd8fab"}]}]}

Bit tricky to understand where you're stuck, but let's give it a shot:
Does the api request actually reach your route (post -> /api), do you see in the network tab a post request to the route?
Have you tried running dd($request->all()) in the controller method so see what you're getting (just do this on the first line inside your method)?
Small gotcha moment:
Sometimes it helps to run the php artisan route:clearcommand

Related

How to post/send an image to an API using Axios?

I am trying to post/send an image to an API through axios.
Here is my frontend code (ReactJS):
const handleImage = (e) => {
const myImg = e.target.files[0];
const config = {
headers: {
"Content-Type": "multipart/form-data"
}
};
if (myImg !== undefined) {
let form = new FormData();
form.append("file", myImg);
axios.post("/upload", form, config)
.then(res => console.log(res))
.catch(err => console.log(err));
}
}
Here is my backend code (NodeJS & ExpressJS):
app.post("/upload", (req, res) => {
console.log(req.body);
res.send(req.body);
});
console.log(req.body) printing an empty object i.e. {} on console window.
So, in short, my doubt is why its printing an empty object? Is there something that I am missing in my code?
This is probably because your backend is not ready to receive files upload.
You can use a library called Multer which will make easier setting up your backend for supporting files upload.
There is a lot of content explaining how to use Multer. You can check this one out

Missing value for stripe.confirmCardPayment intent secret: value should be a client secret of the form ${id}_secret_${secret}

I'm working on an e-commerce web app using Laravel and Vuejs. I chose Stripe's API to accept and manage payments.
In my Vue component, which contains the payment form, and before making it visible(I'm using a multi-step form), I send a post request to my payments store controller function to 'initialize' Stripe and get the clientSecret variable. As follows:
axios
.post('http://127.0.0.1:8000/payments', {
headers: {
'content-type': 'multipart/form-data'
},
'total': this.total,
'mode_payment': this.mode_payment
})
This is how the store function looks like in my PaymentController:
public function store(Request $request)
{
$payment = new Payment;
$payment->montant_payment = $request->total;
$payment->mode = $request->mode_payment;
$payment->date_payment = Carbon::now();
$payment->statut_payment = false;
$payment->save();
Stripe::setApiKey(env('STRIPE_SECRET'));
header('Content-Type: application/json');
$intent = PaymentIntent::create([
'amount' => $payment->montant_payment,
'currency' => 'usd',
]);
$clientSecret = Arr::get($intent, 'client_secret');
$amount = $payment->montant_payment;
return response()->json($clientSecret);
}
As you can see, the store function sends back a JSON response containing the clientSecret variable. This response is then captured by the same Vue component discussed above.
This is how the Vue component captures the response:
.then((response) => {
this.clientSecret = response.data;
var stripe =
Stripe('pk_test_51JL3DSLJetNHxQ3207t13nuwhCB1KPvUJJshapsOQATnZn79vA4wK3p9Hf2yi2uUXfXXWdAtLZGRepfJGvRnd7oI006Kw6rFTC');
document.querySelector("button").disabled = true;
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
}
};
this.card = elements.create("card", { style: style });
this.card.mount("#card-element");
this.card.on('change', ({error}) => {
let displayError = document.getElementById('card-error');
if (error) {
displayError.classList.add('alert', 'alert-warning');
displayError.textContent = error.message;
} else {
displayError.classList.remove('alert', 'alert-warning');
displayError.textContent = '';
}
});
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(ev) {
ev.preventDefault();
// If the client secret was rendered server-side as a data-secret attribute
// on the <form> element, you can retrieve it here by calling `form.dataset.secret`
stripe.confirmCardPayment(String(this.clientSecret), {
payment_method: {
card: this.card,
billing_details: {
name: "testan testo"
}
}
})
.then(function(result) {
if (result.error) {
// Show error to your customer (e.g., insufficient funds)
console.log(result.error.message);
}
else {
// The payment has been processed!
if (result.paymentIntent.status === 'succeeded') {
console.log(result.paymentIntent);
window.location.href = 'http://127.0.0.1:8000/payment-success/';
}
}
});
});
})
Using Vue web dev tools I checked that the clientSecret variable has been successfully passed from the laravel store controller function to the payment Vue component, thanks to the execution of the commmand : this.clientSecret = response.data;.
However, when clicking the pay button, I get the following error in my console:
Uncaught IntegrationError: Missing value for stripe.confirmCardPayment intent secret: value should be a client secret of the form ${id}_secret_${secret}.
at X ((index):1)
at Q ((index):1)
at lo ((index):1)
at (index):1
at (index):1
at e.<anonymous> ((index):1)
at e.confirmCardPayment ((index):1)
at HTMLFormElement.eval (ComLivPay.vue?5598:339)
I guess the problem then is in the next portion of code:
stripe.confirmCardPayment(String(this.clientSecret), {
payment_method: {
card: this.card,
billing_details: {
name: "testan testo"
}
}
})
EDIT:
Since the problem seems to arise from the string form of the this.clientSecret variable. Here's how it looks like in Vue dev tools:

how to upload image in register API in strapi?

In defaut registration API, I need to uplaod the image of user in registration API. So how could I manage it ? I'm sending in a formData and it works fine. I can see (binary) in network.
I tried to add image field and it works in admin panel but from API side I tried to send the file in key names like files, profileImage.
I didn't get the error in res. I got success in res.
Issue: When I reload the admin panel, I didn't get user's profile image.
Try this way. I used in react and it works fine for me.
signUpHandler = () => {
console.log("SignUp data ::: ", this.state);
let data = {
username: this.state.signUpForm.username.value,
phone: this.state.signUpForm.phone.value,
email: this.state.signUpForm.email.value,
password: this.state.signUpForm.password.value
}
axios.post('http://0.0.0.0:1337/auth/local/register', data)
.then(res => {
console.log(res);
return res.data.user.id;
})
.then(refId =>{
const data = new FormData();
data.append('files', this.state.selectedFile);
data.append('refId', refId);
data.append('ref', 'user');
data.append('source', 'users-permissions');
data.append('field', 'profileImage');
return axios.post('http://0.0.0.0:1337/upload', data)
})
.then(res =>{
console.log(res);
alert("You registered successfully...");
this.props.history.push('/login');
})
.catch(error =>{
console.log(error);
})
}
First, you will have to customize your user-permission
To do so, you will have to understand this concept: https://strapi.io/documentation/3.0.0-beta.x/concepts/customization.html
Then you will have to find the function you want to update - in your case, the register function.
And tada here it is https://github.com/strapi/strapi/blob/master/packages/strapi-plugin-users-permissions/controllers/Auth.js#L383.
So you will have to create ./extensions/users-permissions/controllers/Auth.js with the same content as the original file.
Then you will have to add
const { parseMultipartData, sanitizeEntity } = require('strapi-utils');
const uploadFiles = require('strapi/lib/core-api/utils/upload-files');
on the top of your file.
And in your function use this
const { data, files } = parseMultipartData(ctx); to parse data and files.
Then you will have to replace ctx.request.body by data to make sure to use the correct data.
After that you will have to add this after the user creation line
https://github.com/strapi/strapi/blob/master/packages/strapi-plugin-users-permissions/controllers/Auth.js#L510
if (files) {
// automatically uploads the files based on the entry and the model
await uploadFiles(user, files, { model: strapi.plugins['users-permissions'].models.user })
}
Solution for Strapi v4:
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer XXXX");
var formdata = new FormData();
formdata.append("files", fileInput.files[0], "XXX.png");
formdata.append("refId", "46");
formdata.append("field", "image");
formdata.append("ref", "plugin::users-permissions.user");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: formdata,
redirect: 'follow'
};
fetch("http://localhost:1337/api/upload", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));

Add custom headers to upload image

I'm currently trying to integrate the CKeditor 5 ReactComponent into my app.
I'm facing an issue with the upload image functionality... I use a Node/Express backend which uses a JWT auth middleware, so each request must have an Authorization header in order to pass.
I want to know if one of the following is possible:
a way to add a custom header to the component
a way to overwrite the upload handler and call a custom handler instead in which I can do what ever
Below is my code
<CKEditor
editor={ClassicEditor}
data="<p>Add product description here</p>"
onInit={(editor) => {
// You can store the "editor" and use when it is needed.
//console.log('Editor is ready to use!', editor);
}}
onChange={(event, editor) => {
const data = editor.getData();
this.handleData(data)
}}
config={{
ckfinder: {
uploadUrl: `${apiUrl}/upload/images/description`,
},
}}
/>
Thanks
try it with this code in property onInit
onInit={ editor => {
editor.plugins.get( 'FileRepository' ).createUploadAdapter = function( loader ) {
return new UploadAdapter( loader );
};
}}
after you must create the class UploadAdapter
class UploadAdapter {
constructor( loader ) {
// Save Loader instance to update upload progress.
this.loader = loader;
}
upload() {
const data = new FormData();
data.append('typeOption', 'upload_image');
data.append('file', this.loader.file);
return new Promise((resolve, reject) => {
axios({
url: `${API}forums`,
method: 'post',
data,
headers: {
'Authorization': tokenCopyPaste()
},
withCredentials: true
}).then(res => {
console.log(res)
var resData = res.data;
resData.default = resData.url;
resolve(resData);
}).catch(error => {
console.log(error)
reject(error)
});
});
}
abort() {
// Reject promise returned from upload() method.
}
}

Laravel vue show old data on update fields

So I've made update function for my component and it's working perfectly the only issue is I cannot show old data (if there is any) to the user,
This is what I have now:
As you see not only i can send my form data to back-end for update, but also I have the saved data already.
Code
export default {
data: function () {
return {
info: '', //getting data from database
profile: { //sending new data to back-end
photo: '',
about: '',
website: '',
phone: '',
state: '',
city: '',
user_id: '',
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
}
}
},
mounted: function() {
this.isLoggedIn = localStorage.getItem('testApp.jwt') != null;
this.getInfo();
},
beforeMount(){
if (localStorage.getItem('testApp.jwt') != null) {
this.user = JSON.parse(localStorage.getItem('testApp.user'))
axios.defaults.headers.common['Content-Type'] = 'application/json'
axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('testApp.jwt');
console.log()
}
},
methods: {
update() { // sending data to back-end
let user_id = this.user.id;
let photo = this.profile.photo;
let about = this.profile.about;
let website = this.profile.website;
let phone = this.profile.phone;
let state = this.profile.state;
let city = this.profile.city;
axios.put('/api/updateprofile/'+ user_id, {user_id, photo, about, website, phone, state, city}).then((response) => {
this.$router.push('/profile');
$(".msg").append('<div class="alert alert-success" role="alert">Your profile updated successfully.</div>').delay(1000).fadeOut(2000);
});
Vue.nextTick(function () {
$('[data-toggle="tooltip"]').tooltip();
})
},
getInfo: function() { //getting current data from database
let user_id = this.user.id;
axios.get('/api/show/'+ user_id).then((response) => {
this.info = response.data;
console.log(response);
});
},
}
}
Component sample field
// this shows my about column from database
{{info.about}}
// this sends new data to replace about column
<textarea name="about" id="about" cols="30" rows="10" class="form-control" v-model="profile.about" placeholder="Tentang saya..."></textarea>
Question
How to pass old data to my fields (sample above)?
Update
Please open image in big size.
This can be done by assigning this.profile the value of this.info on your Ajax response.
This way you will have input fields set up with original values.
function callMe() {
var vm = new Vue({
el: '#root',
data: {
profile:{},
info:{}
},
methods: {
getInfo: function() { //getting current data from database
this.info={about:"old data"}//your response here
this.profile=Object.assign({},this.info);
},
},
})
}
callMe();
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.11/dist/vue.js"></script>
<div id='root'>
<button #click="getInfo">Ajax Call click me</button>
Input <input v-model="profile.about"/>
<p>profile:{{this.profile}}</p>
<p>info: {{this.info}}</p>
</div>
The problem with the code is that after assigning new value info is not reactive anymore. You need to keep "info" like this in the start.
info: { // data from api
photo: '',
about: '',
website: '',
phone: '',
state: '',
city: '',
user_id: '',
}
And after fetching values from api update each value separately.
getInfo: function() { //getting current data from database
let user_id = this.user.id;
axios.get('/api/show/'+ user_id).then((response) => {
this.info.photo = response.data.photo;
this.info.about = response.data.about;
//all other values
console.log(response);
});
},
In your textarea you have a model profile.about, the way to show the "old data", is to assing to that model the data
in the create or mounted method you have to assing like
this.profile.about = this.info.about
this way profile.about will have the data stored in your db, that way if the user update it, the old data will be keep safe in this.info.about and the edited in this.profile.about

Resources