I'm trying to submit a form via ajax to update a field of an entity, but do not know how to retrieve the data from the controller:
<form class="ajax" action="{{ path('ajax_setSocial') }}" method="post" {{ form_enctype(form) }}>
<div class="editor">
{{ form_errors(form) }}
<div class="editLabel pls">{{ form_label(form.ragSocial) }}</div>
<div class="editField">
<div class="ptm">
{{ form_widget(form.ragSocial) }} {{ form_errors(form.ragSocial) }}
</div>
{{ form_rest(form) }}
<div class="mtm">
<button class="btn btn-primary disabled save" type="submit">Save</button>
<button class="btn ann">Close</button>
</div>
</div>
</div>
var url = Routing.generate('ajax_setSociale');
var Data = $('form.ajax').serialize();
$.post(url,
Data
, function(results){
if(results.success == true) {
$(this).parents('ajaxContent').remove();
$(this).parents('.openPanel').removeClass('openPanel');
} else {
alert('False'); //test
}
});
controller (ajax_setSocial route)
public function setSocialeAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
// $id = $request->get('form'); ???
$entity = $em->getRepository('MyBusinessBundle:Anagrafic')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Anagrafic entity.');
}
$form = $this->createFormBuilder($entity)
->add('ragSocial', 'text', array('label' => 'Social'))
->add('id', 'hidden')
->getForm();
$form->bind($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
$output = array();
$response = new Response();
$output[] = array('success' => true);
$response->headers->set('Content-Type', 'application/json');
$response->setContent(json_encode($output));
return $response;
}
As recovery values and then pass the id to create the query and the other values to update the entity?
And if the fields do not pass validation, how do I pass the error?
I suggest to pass the id to the controller.
html:
<form class="ajax" action="{{ path('ajax_setSocial', { 'id': entity.id }) }}" method="post" {{ form_enctype(form) }}>
var url = "{{ path('ajax_setSocial', { 'id': entity.id }) }}";
The controller annotation, parameter, and return value, to get the id:
/**
*
* #Route("/{id}", name="ajax_setSocial")
* #Method("POST")
*/
public function setSocialeAction(Request $request, $id) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('MyBusinessBundle:Anagrafic')->find($id);
return array(
'entity' => $entity
);
}
Passing error back to html is like this:
// dummy line to force error:
// $form->get('ragSocial')->addError(new FormError("an error message"));
if ($form->isValid()) {
...
} else {
$errors = $form->get('ragSocial')->getErrors(); // return array of errors
$output[] = array('error' => $errors[0]->getMessage()); // the first error message
$response->headers->set('Content-Type', 'application/json');
$response->setContent(json_encode($output));
return $response;
}
I think you want this:
symfony2 chained selectors
However, this one may also be useful:
Many-to-Many Ajax Forms (Symfony2 Forms) (Answer 3)
Related
This is my post page:
<template>
<br/>
<pagination
#pageClicked="getPage($event)"
v-if="this.getdArray.data != null"
:links="this.getdArray.links"
/>
<a
:href="route('create',{region1:this.region})"
class="list-group-item list-group-item-action w-25 float-right text-gray-200 align-text-bottom"
style="background-color: rgb(00, 00, 00)"
>
글작성
<!-- {{ this.getdArray }} -->
</a>
<div v-for="post in this.getdArray.data" :key="post.id">
<a
:href="route('show',{'id': post.id},{region1:this.region})"
class="list-group-item list-group-item-action w-100 float-right text-gray-200 "
style="background-color: rgb(50, 60, 60)"
>
{{post.title}}
</a>
</div>
</template>
<script>
import {InertiaLink} from "#inertiajs/inertia-vue3";
import Pagination from "./Pagination.vue";
export default {
data(){
return{
postdata: []
}
},
methods: {
check(){
console.log(this.postRegion);
},
getPage(url) {
axios
.get(url)
.then((res) => {
this.getdArray = res.data;
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
},
},
props: [
'regionN', 'posts','dArray'
],
data() {
return {region: "", getposts: [],getdArray:[]}
},
beforeMount() {
this.region = this.regionN;
this.getposts = this.posts;
this.getdArray=this.dArray;
// console.log(this.getposts);
console.log(this.region);
console.log(this.getposts);
console.log(this.getdArray);
},
components: {
InertiaLink,
Pagination
},
computed: {
postRegion: function () {
return this
.getposts
.filter((post) => {
if (post.region == this.region) {
return post
}
});
}
//computed 로 필요한 데이터만 반환
//.filter 함수는 배열에 적용가능하며 배열내 각 원소에 적용
}
}
</script>
And this is my pagination page:
<template>
<div v-if="links.length > 3">
<div class="flex flex-wrap -mb-1">
<template v-for="(link, key) in links">
<div
v-if="link.url === null"
:key="key"
class="px-4 py-3 mb-1 mr-1 text-sm leading-4 text-gray-400 border rounded"
v-html="link.label"
/>
<div
v-else
#click="pageClicked(link.url)"
:key="link.url"
class="px-4 py-3 mb-1 mr-1 text-sm leading-4 border rounded hover:bg-white focus:border-indigo-500 focus:text-indigo-500"
:class="{ 'bg-white': link.active }"
:href="link.url"
v-html="link.label"
></div>
</template>
</div>
</div>
</template>
<script>
export default {
props: {
links: Array,
},
methods: {
pageClicked(url) {
this.$emit("pageClicked", url);
},
},
};
</script>
When I push list button,
something like
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title inertia>Laravel</title>
This kind of thing send by res.data
How can I fix it? On other people's code, res.data has data and link,
but on my res.data, HTML code send back.
this is my controller
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Storage;
use Inertia\Inertia;
use Symfony\Component\Console\Input\Input;
class PostController extends Controller
{
// public function index()
// {
// //
// }
public function create(Request $request)
{
$region1 = $request->region1;
return Inertia::render('Create', ["region1" => $region1]);
}
public function store(Request $request)
{
$request->validate([
'title' => 'required|min:3',
'content' => 'required|min:3',
'image' => 'image',
// 'region1' => 'required'
]);
// $user_id = Auth::user()->id;
// $region =
// $request->region1;
$post = new Post();
$post->user_id = Auth::user()->id;
$post->title = $request->title;
$post->content = $request->content;
//inertia 에서의 post요청
//function storePost() {
//form.post('/post/store')
//}
//와 같이.
$path = $request->file('image')->store('image', 'public');
$imgpath = "/storage/" . $path;
$post->image = $imgpath;
$post->region = $request->region;
$post->save();
// $validated = array_merge($validated, ['image' => $imgpath]);
// $validated = array_merge($validated, ['user_id' => $user_id]);
// $validated = array_merge($validated, ['region' => $region]);
// Post::create($validated);
// if ($request->hasFile('image')) {
// $image_path = $request->file('image')->store('image', 'public');
// }
// $post = new Post();
// $post->title = $request->title;
// $post->content = $request->content;
// $post->user_id = Auth::user()->id;
// $post->region = $request->region;
// $post->image = "/storage/" . $image_path;
// $posts = Post::all();
// $b2018 = DB::table('crimes')->get();
// $t2018 = json_decode(json_encode($b2018), true);
// $region1 = $request->region1;
// $posts = Post::find($post->id);
return Redirect::route('main');
}
public function show($id)
{
$posts = Post::find($id);
return Inertia::render('Show', ["posts" => $posts]);
}
public function edit($id)
{
$post = Post::find($id);
return Inertia::render('Edit', ["post" => $post]);
}
public function update(Request $request, $id)
{
$post = Post::find($id);
$post->title = $request->title;
$post->content = $request->content;
$post->user_id = Auth::user()->id;
if ($request->hasFile('image')) {
$fileName = time() . '_' . $request->file('image')->getClientOriginalName();
$request->file('image')->storeAs('public/images', $fileName);
if ($fileName) {
$input = array_merge($input, ['image' => $fileName]);
}
}
$post->save();
$posts = Post::find($id);
return Inertia::render('Show', ["posts" => $posts]);
}
public function destroy($id)
{
$post = Post::find($id);
$post->delete();
if ($post->image) {
Storage::delete('public/images/' . $post->image);
}
return Inertia::render('question');
}
}
i added my controller, and i tried to fix it but it still sent request twice...
I think problem is in your Laravel back-end or bad request in front-end side, sometime laravel return error in HTML format, if you not defined accept application/json in your request headers.
Try to resolve that problem first, and edit your question.
I'm using a spatie/laravel-flash for displaying some flash messages. It works when I use simple HTML forms, but when I use Vue.js templates, the message doesn't show. (and sometimes they don't) after submitting the form and go to the next request.
main layout
<div class="col-lg-12 mb-2">
#include('layouts.partials.flash_message')
</div>
<section class="py-5">
#yield('content')
</section>
layouts.partials.flash_message
#if(flash()->message)
<div class="{{ flash()->class }} alert-dismissible" role="alert">
<button type="button" class="close close-white" data-dismiss="alert">×</button>
{{ flash()->message }}
</div>
#endif
create.blade.php
#extends('layouts.main_layout')
#section('content')
<create-school> </create-school>
#endsection
Vue.js template store() method
store()
{
axios.post('/master/schools', {
name: this.name,
}.then((response) => {
this.name = ''
}));
}
Laravel store method
<?php
public function store(Request $request)
{
$school = School::create([
'name' => $request->name,
]);
flash('success message', 'alert alert-success');
return back();
}
Since you are using ajax to store your data, you will need to provide the message as a response in that request. So basically the store method on your controller would look like
public function store(Request $request)
{
$school = School::create([
'name' => $request->name,
]);
return response($school, 201);
}
201 is the HTTP code to indicate a resource was created, and the by the RFC you should return the resource in the response.
In your Vue file, the store method would then be
store()
{
axios.post('/master/schools', {
name: this.name,
}.then((response) => {
this.name = ''
// Modify your DOM or alert the user
alert('Resource created')
}));
}
As a recomendation, you should aways validate user input before storing.
This is probably a very simple thing, but for some reason I just can't figure it out. I created a function that gets the images from my vue component.
What I'm trying to do is take the images from my postImage() and have them in my store() function, so that I can save everything into the database.
The problem I'm getting is when I do that I get this error
Too few arguments to function App\Http\Controllers\Admin\CategoryController::store(), 1 passed and exactly 2 expected
I do understand that the error is telling me that only the $request was sent and not the $image. I'm not sure how to get it working. If I've left anything out please let me know
Here is my controller
public function store(Request $request, $image)
{
$category = new Category();
$input = $this->safeInput($request);
$category->fill($input);
dd($image);
$slug = $category->slug($category->title);
$category->slug = $slug;
if($request->has('active'))
{
$category->active = 1;
}else{
$category->active = 0;
}
$category_order = $category->order_number();
$category->order = $category_order;
$category->save();
}
public function postImage(Request $request)
{
if($request->hasFile('image'))
{
$names = [];
foreach($request->file('image') as $image)
{
$destinationPath = 'product_images/category/';
$filename = $image->getClientOriginalName();
$image->move($destinationPath, $filename);
array_push($names, $filename);
}
$image = json_encode($names);
return $image;
}
}
This is my vue component
<template>
<div class="container">
<div class="uploader"
#dragenter="OnDragEnter"
#dragleave="OnDragLeave"
#dragover.prevent
#drop="onDrop"
>
<div v-show="!images.length" :value="testing()">
<i class="fas fa-cloud-upload-alt"></i>
<div>OR</div>
<div class="file-input">
<label for="file">Select a file</label>
<input type="file" id="file" #change="onInputChange" multiple>
</div>
</div>
<div class="images-preview" v-show="images.length">
<div class="img-wrapper" v-for="(image, index) in images">
<img :src="image" :alt="`Image Uplaoder ${index}`">
<div class="details">
<span class="name" v-text="files[index].name"></span>
<span class="size" v-text="getFileSize(files[index].size)"></span>
</div>
<div class="btn btn-danger" #click="funDeleteFile(index)">
Remove
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
data() {
return {
isDragging: false,
//Sets the dragCount to 0
dragCount: 0,
//Makes files an array, so that we can send the files to the server
files: [],
//Makes images an array, so that we can let the user see the images
images: [],
}
},
methods: {
testing() {
console.log('This is submit images - '+this.files);
var formData = new FormData();
this.files.forEach(file => {
formData.append('image[]', file, file.name);
});
axios.post('/admin/category/post-image', formData);
},
OnDragEnter(e) {
//Prevents the default action of the browser
e.preventDefault();
// This lets the dragCount become 1, so that the image uploader changes colour
this.dragCount++;
// Changes the isDragging variable to true instead of false
this.isDragging = true;
return false;
},
OnDragLeave(e) {
//Prevents the default action of the browser
e.preventDefault();
// This lets the dragcount become 0, so that the image uploader changes to it's original colour
this.dragCount--;
// This is if the dragCount is <= 0 then the isDragging variable is false
if (this.dragCount <= 0)
this.isDragging = false;
},
onInputChange(e) {
// Grabs the files from the event
const files = e.target.files;
// Creates an array for files, so that we can loop thru it
// Send the file to the addImage method via "this.addImage(file)"
Array.from(files).forEach(file => this.addImage(file));
},
onDrop(e) {
//Prevents the default action of the browser
e.preventDefault();
//Stops the propagation into the other elements inside the one we drop and file into
e.stopPropagation();
// This is to disable the dragging of the images
this.isDragging = false;
// Grabs the files from the event
const files = e.dataTransfer.files;
// Creates an array for files, so that we can loop thru it
// Send the file to the addImage method via "this.addImage(file)"
Array.from(files).forEach(file => this.addImage(file));
},
addImage(file) {
//Checks if the file type is an image
if (!file.type.match('image.*')) {
this.$toastr.e(`${file.name} is not an image`);
return;
}
this.files.push(file);
const img = new Image(),
reader = new FileReader();
reader.onload = (e) => this.images.push(e.target.result);
reader.readAsDataURL(file);
},
}
}
</script>
my create.blade.php
#extends('layouts.admin')
#section('content')
#component('admin.components.products.category-form', [
'formUrl' => route('category.store'),
'formMethod' => 'POST',
'model' => $category,
'category_id' => $category_id,
'image' => '',
'image2' => ''
])
#endcomponent
#endsection
and my form
{{ Form::model($model, array('url' => $formUrl, 'method' => $formMethod, 'class' => 'add-form', 'files' => true)) }}
<div class="form-group">
{{ Form::label('category_id', 'Parent Category') }}
{{ Form::select('category_id', $category_id->prepend('Please Select', '0'), null, array('class' => 'form-control')) }}
</div>
<div class="form-group">
{{ Form::label('title', 'Title') }}
{{ Form::text('title', null, array('class' => 'form-control')) }}
</div>
<div class="form-group">
<label>Active:</label>
{{ Form::checkbox('active', 0) }}
</div>
<div id="app" class="mb-20">
<category-image></category-image>
</div>
<div class="form-group">
{{ Form::submit('Save', array('class' => "btn btn-dark btn-lg btn-block")) }}
</div>
{{ Form::close() }}
My routes
Route::resource('admin/category', 'Admin\CategoryController');
Route::post('admin/category/post-image', 'Admin\CategoryController#postImage')->name('admin.category.post-image');
UPDATE
I've tried this to pass the image to a hidden field in my form so that I can grab it in the $request in my store function.
In my CategoryController#create
$category = new Category();
$category_list = Category::with('parentCategory')->get();
$category_id = Category::pluck('title', 'id');
// I've added this.
$image = '';
return view('admin.products.category.create', compact('category', 'category_list', 'category_id', 'image'));
in my CategoryController#postImage
//I've added this to, so that I can pass the image variable to the create.blade.php
return redirect()->route('category.create', compact('image'));
then in my create.blade.php I added
'my_image' => $my_image
and in my category-form.blade.php component I added
<div id="app" class="mb-20">
<category-image></category-image>
<input type="hidden" name="image" id="image" value="{{ $my_image }}">
</div>
at the moment I haven't been able to do that either. Though I'm not sure if this is the right way to go, I'm a bit worried that some random person can then add whatever they want by using the hidden input
For what do you have the parameter $image? This is not specified in your axios.post.
public function store(Request $request)
{
$category = new Category();
$input = $this->safeInput($request);
$category->fill($input);
dd($this->postImage($request));
$slug = $category->slug($category->title);
$category->slug = $slug;
if($request->has('active'))
{
$category->active = 1;
}else{
$category->active = 0;
}
$category_order = $category->order_number();
$category->order = $category_order;
$category->save();
}
public function postImage($request)
{
if($request->hasFile('image'))
{
$names = [];
foreach($request->file('image') as $image)
{
$destinationPath = 'product_images/category/';
$filename = $image->getClientOriginalName();
$image->move($destinationPath, $filename);
array_push($names, $filename);
}
$image = json_encode($names);
return $image;
}
}
If the $request is available there, Then there is no need to pass extra $image variable.
have you tried
dd($request)
or
print_r($request->toArray()); exit;
for see what's in your request!
In your create.blade you use 'formUrl' => route('category.store'), this route calls the "store" method, right? If so, it also needs to pass the $image parameter. It would be easier to identify the problem if we could se your web routes file too.
If route('category.store') does call the store method you have a few options.
1 - If you don't really need the $image parameter for the store method, you could just remove it.
2 - If you need it in a few cases, just make the parameter optional and check if it's received before handling it. Example: store(Request $request, $image = null)
3 - If this parameter actually is required, you will have to pass it everytime, even when calling routes. Example: route('category.store', ['image' => $something]). Looking at your code at this moment in create.blade you don't have the content to pass though, so I don't think this is an option.
The problem isn't the image missing in the request object sent through the form, it is the second parameter required by the category.store method.
Even if you now send the image in the form with a hidden field, you would still need to pass it as a parameter everytime you call the category.store.
Your store method is defined like
store(Request $request, $image)
So, when you call this method, even if you're just getting the route URL with route('category.store'), you do need to send the image parameter in this call.
Example:
route('category.store', ['image' => 'image id here']);
The same goes for the route definition in your web routes file. You're using a resource route, but laravel don't expect a second parameter for the store method in a default resource, so you will need to change that.
/*
adds exception to the resource so it will not handle the store method
*/
Route::resource('admin/category', 'Admin\CategoryController')->except(['store']);
//adds a custom route that supports the $image parameter.
Route::post('admin/category/{image}', 'Admin\CategoryController#store')
Now, if you're planning to send the image through the request object, you don't need it as a second parameter, so the only thing you will need to change is to make your category.store method like that.
public function store(Request $request)
i want to get from the data base users after filtring them by city ( i got city and user as entities) here is my code i tried it at the first time it worked , but the i got this error :
Variable "users" does not exist in otaotaBundle:Evenement:invitEvt.html.twig at line 20
here is my controller
public function invitEvtAction($id)
{
//the city's form
$form = $this->get('form.factory')->create(new PaysType());
$request=$this->getRequest();
if ($request->getMethod() == 'POST')
{
$city= $form->get('pays')->getData();
//get user by city
$users = $this->getDoctrine()
->getManager()
->getRepository('otaUserBundle:User')
->selectVoisin($city);
//send mail ....
}
return $this->render('otaotaBundle:Evenement:invitEvt.html.twig', array(
'form' => $form->createView(),
'users'=>$users,
));
}
and the view html.twig
<form method="post" novalidate>
<div>
{{ form_widget(form.pays) }}
</div>
<div class="CSSTableManipule" >
<table>
{% for us in users %}
{# ... #}
{% endfor %}
</table>
</div>
<br/><br/>
<button type="submit">Envoyer</button>
</form>
and the city's form :
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('pays', 'entity', array(
'class' => 'otaUserBundle:Pays',
'property' => 'pays', ));
}
any help :/ .??
Try this instead (assuming voisin is a property of the User entity and may equal the value of $city for one or more records):
$users = $this->getDoctrine()
->getManager()
->getRepository('otaUserBundle:User')
->findAll(['voisin' => $city]);
I am new in AJAX. but I am trying to learn How this is working.
I am using symfony2 with fos user bundle and I want implement AJAX to my login form.
so I was doing this :
login.html.twig
<script>
$('#_submit').click(function(e){
e.preventDefault();
$.ajax({
type : $('form').attr( 'method' ),
url : $('form').attr( 'action' ),
data : $('form').serialize(),
success : function(data, status, object) {
if (data.sucess == false) {
$('.tab-1').prepend('<div />').html(data.message);
} else {
window.location.href = data.targetUrl;
}
}
});
</script>
<div id="tab-1" class="login_form">
<form action="{{ path("fos_user_security_check") }}" role="form" method="post">
<label for="username"><strong>User Name / Email Address</strong>
<input type="text" id="username" name="_username" value="{{ last_username }}" required="required" />
</label>
<label for="password"><strong>Password</strong>
<input type="password" id="password" name="_password" required="required" />
</label>
<label for="password"><strong>Remember Me</strong>
<input type="checkbox" id="remember_me" name="_remember_me" value="on" />
</label>
<input type="submit" class="submitBut" id="_submit" name="_submit" value="{{ 'security.login.submit'|trans({}, 'FOSUserBundle') }}" />
</form>
</div>
And when submit then go this file :-
<?php
namespace XXXX\UserBundle\Handler;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;
class AuthenticationHandler implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{
protected $router;
protected $security;
protected $userManager;
protected $service_container;
public function __construct(RouterInterface $router, SecurityContext $security, $userManager, $service_container)
{
$this->router = $router;
$this->security = $security;
$this->userManager = $userManager;
$this->service_container = $service_container;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token) {
if ($request->isXmlHttpRequest()) {
$result = array('success' => true);
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
else {
// Create a flash message with the authentication error message
$request->getSession()->getFlashBag()->set('error', $exception->getMessage());
$url = $this->router->generate('fos_user_security_login');
return new RedirectResponse($url);
}
return new RedirectResponse($this->router->generate('anag_new'));
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) {
$translator = new Translator('fr_FR');
//$result = array(
// 'success' => false,
// 'function' => 'onAuthenticationFailure',
// 'error' => true,
// 'message' => $this->translator->trans($exception->getMessage(), array(), 'FOSUserBundle')
//);
$result = array('success' => false);
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
When submit the form then show me in login_check url:
{"success":false}
But I want when result false then return same form where I was trying to login(I mean same popup div)?
What's wrong my code ajax or action return ?
Or I am return correct ?
window.location will reload the entire page. That's not the desired result I suppose since you are using AJAX ( the hole point of AJAX is to not reload the page) instead you could display an error message if the login is not successful.
I suggest you add an error div in your html form
<div class='error' style="display:none" > ooups an erro occured </div>
and then in the ajax call just show it or add a significant message error :
if (data.sucess == false) {
$('.tab-1').prepend('<div />').html(data.message);
} else {
$('.error').show();
}