Your page has no way to know that you've set a Session variable. With session variables, just like with persisting something to a database, you need to have that variable represented as data on the front end as well. Then you can pass that value from the back end to your 'ajax success' and set your js variable with that value. Now Vue can watch that data and make changes to the dom as it changes. Hope that made sense and I hope I was understanding your problem correctly.
Session variable not setting in controller when called by ajax
Hey, I have a quick issue I haven't been able to get past.
I have a simple voting form that should only be displayed when the visitor has not voted on an issue. I'm using vue, and vue-resource to make an ajax call to update the database and validate and everything seem to run smoothly. I've opted for a simple session variable to make sure that the visitor does not see the poll repeatedly.
Everything works except for the one session call which seems to just do nothing.
laravel 5.1.36, vue 1.0.24, vue-resource 0.70
NetworkVoteController function vote
public function vote(Request $request){
// ADD CUSTOM WEIGHTING METRICS
if($this->isDisqualifiedVote($request->ip())){
return response()->json(['message' => 'Unable to vote multiple times / day', 'success' => false]);
}else{
Session::put(['voted' => true]); //just doesn't seem to lock it in.
$this->placeVote($request);
return response()->json(['message'=>'Thank You!', 'success'=>true]);
}
}
I've injected this code just to double check in the actual view before the test and there, it does seem to work. Here is an example, if I refresh this page, it will place the session variable and hide the voting section
@if(! session('voted'))
@include('NewDesignPages.network-vote')
@endif
<pre><?php
Session::put(['voted' => true]);
var_dump(session()->all());
?></pre>
Any ideas?
Thank -Roni
Hi @clay,
Let me elaborate, because I think your answer may have confused me. When I make an AJAX request with a vue directive, or jQuery, (although I don't know jQuery, but it's a guess), my ajax request is making a post request to the app. This post request is firing on my routes, where in turn it's being handled by Laravel PHP controller. The code thats running in function vote is actually PHP code specifically invoked in the laravel framework.
And it's behaving exactly as expected, it's checking the validity of the user vote through eloquent and persisting the appropriate data, and finally sending back appropriate JSON.
Now, the JSON is coming back to the vue-directive through vue-resource, and firing the complete or error methods. Those are removing the DOM right now... and thats all good.
The problem is when I come BACK to the page or refresh, the option to vote is there again. Now I'm controlling proper validation and rules through the repository, but I think it's confusing to the end user if they happen to go to that page a second time, and see the vote option again. It should revert back to it's normal state.
To accomplish this, it seemed like a good idea to set a session. The only issue is the session is NOT being set even though the code is correct! I could also control this through eloquent but, I just want to know what could be happening in the session.
So if you are working in an AJAX state, do you have to prime the session? If everything else is working shouldn't the session also be working?
-Roni
Its not a bug. The AJAX call happens after the page load, your javascript is being rendered when the session is in state A. Once you make the AJAX call, it is in state B – but by that point the call to Session::get() has already been made and the initial view has already been rendered.
So you have to send the session back as a json response.
That was my point, again, if I'm completely understanding the issue.
Thanks again for your advice, but thats not the confusing part. I still don't have a session solution, but I've solved this particular situation through eloquent.But I'd really like to figure this out for future reference.
@clay, When you state this
"So you have to send the session back as a json response."
Do you mean I have to re-assign the session after I get the JSON response back always? To me that sounds odd, because why would I use a session in the first place instead of a variable or a DTO? To clarify, I've co-opted your analogy to crystalize this issue... I hope
UDPATE: just updated type-o on state
STATE A : the form is shown on the initial render. (working)
STATE B.0: the ajax request is called, using a vue directive, (working)
STATE B.1: I set the session variable. ( somehow not working)
STATE B.2: I get the response from AJAX and if appropriate,I take action, in this case removing the form right there using vue, v-show, and that works. (working)
STATE B.3: I leave the page, or refresh or do something that exits this current state.
STATE C.0: I RE-ENTER the page. refresh, leave and come back ... something.
STATE C.1: the page loads checks the session, and the session I set in state B.1 is somehow not set.
The question is why would a session not get set? In a php page call when it's called in the background by AJAX.
I didn't solve this with sessions, since the rest of the API works as expected,
I added this check to the model:
public static function canVoteNow($ip) {
$vote = self::where('ip_address','=',$ip)
->where('created_at','>=',Carbon::now()
->subDay(1)->toDateTimeString())->count();
return (! (bool) $vote);
}
And then I changed the above test to this:
@if(\App\NetworkVote::canVoteNow(Request::ip()))
@include('NewDesignPages.network-vote')
@endif
However there are situations where the session is the only feasible solution. So I would really like to figure out how to set a session var in an ajax call from a form action.
Thanks again
cheers,
-Roni
Well, we mean that you dont have to "set the session" after the response with vue, because php/laravel Session::get() wont work here because.. Well it is php ^^
If Session or not is not the point, dont know if i would go with your session way
- Guard the request so that only a User who is able to $user->canVote() can vote hust ;)
- set a default data { showVote: true } on the vue site
- then set the vote partial/div/html stuff for voting to <div ... v-show="showVote"> ..
- handle the showVote Data stuff in your ajax request success and its gone
- to handle the reload page simple only show the div @ 3 if the user->canVote()
- OR maybe im to drunk for this atm ^^
@Roni The way sessions works is with a session ID stored in a COOKIE. I believe that when you are making an ajax request with vue-resource it doesn't bundle the cookies you have stored in the website.
Is it a cross-domain ajax request?
@samo, thanks I'm kid of doing that with a repository method of checking against the DB and then deciding to show the partial or not based a simple PHP if, I could use v-show, I supposed I'd just have to bind my data value to the data el. But I think this is a worse way to handle it because I'm always loading images and cells into the DOM that I don't need and then hiding them. I'd rather do that server side. I'm sure vue can do such things, but I'm not good enough in JS to make something like an SPA yet. Also, I'm setting the session IN php, my ajax is simply calling a php file, and one that should have access to the session. It has access to the request object (laravel Request, Illuminate\Http\Request) Which itself has Session. But I do have session Facade pulled in as well so either Session::put or $request->session()->put should both be valid unless my voodoo isn't strong enough.
@kfirba, THANK YOU!!! I was at the point of repeating "there is no spoon" over and over again...
It isn't cross domain, it's a simple tally for our customers to see where they wold like us to grow, so as the managers make expansion decisions, they can add a metric for customer requests. So it's a regularly used page, but we've added a form part way through to give them a direct line to the president essentially. The problem is you get one vote, if you are logged in it's massively weighted, if it's from a user who isn't on our service, we still add their input. What we don't want is spammed suggestions. So we throttle it to 1 / day for non logged in users. Right now, I'm blocking by IP, but I'd rather block by a session or cookie, so for example multiple users could go to a coffee shop or different people at an office can have different votes. Right now, its an effective tool but it could be made better.
If I run this page without ajax as a request utility it does work. However the use case DOES NOT allow me to leave the page. So I need to accomplish this by ajax. I assumed the session or cookie would be optimal.
If you think of anything please don't hesitate to drop me a line. I'm sure a ton of us JS and VueJS newbs would greatly benefit by figuring this out.
-Roni
I put this together quickly and on my phone, but maybe there's something here you can use. Basically, I would hit a route when the page initially loaded that checks the vote status. If a vote status exists, send it in the response. You can then use the response to set a flag on your front-end.
When you vote, you can set the 'voted' session variable. Although you will not have access to the session without reloading the page, upon success in your ajax call, you can set your 'voted' flag to true.
So now, you'll always know the status of 'voted', whether it's when a page is reloaded, upon initial page load, or after voting.
Again, maybe I'm way off base here, but it seems like this would work for what you need.
Html:
<my-element v-if="!voted">Don't show this if I've already voted</my-element>
Javascript:
new Vue({
el: '#app',
components: { },
data: {
voted: false,
exampleData: []
},
methods: {
vote() {
Vue.http.post(/vote, exampleData).then(function(response){
this.voted = true;
// do something with the response if needed
}, function(error){
});
},
checkVoteStatus(){
Vue.http.get(/voted).then(function(response){
this.voted = response.data;
}, function(){
}
},
ready(){
this.checkVoteStatus();
}
}
});
Php:
//votecontroller // route post '/vote'
public function store(){
// process the vote
session()->put('voted', 'somevalue');
return response()->json('Success!', 201);
}
// route get '/voted'
public function status(){
$voted = session()->has('voted') ? true : false;
return response()->json($voted, 200);
}
May not be exactly right, but should be close to working code.
Hi @clay,
Let me give it a try and see if I can make it work. The key there might be reading the session variable from an ajax session, I'm going to add one step to the experiment, right now, I'll put back the code to set the session in my ajax call, and then I'll see if I can do a simple dd($request->session()-all()); And see what gets spit out there.
@kfirba, intimated that there were no cookies in ajax which was what was causing the issue of session state continuity. I have a few hours now to pound on this, so I'll see if I can beat up .
I've never successfully created an component that had an ajax call inside it, I thought that was why I had to use a vue directive, if you have a chance, if you have any old examples of that on JSFiddle or something like that, that would be super helpful.
Thanks again for your continued efforts.
-Roni
after i made the post, i set up a quick test app and it works as expected. the fact that an ajax request is making the request has nothing to do with it. Once you get inside your method in your controller, you set the session variable there. Your javascript never checks the session. Read through the code carefully. Walkthrough:
- Initial page load
- ajax request hits a route, which in turn calls a method on a controller, which checks to see in a session variable has been set. If it has, it returns true to your javascript, if it has not, it returns false.
- Set a javascript variable with the value returned from the ajax request (either true or false).
- You can use that variable to determine whether or not to show the html element that gives someone the ability to vote and you can even use it in your vote method to determine if you even hit your api to allow a vote.
- When someone votes, after returning to the 'onsuccess' portion of your ajax call, simply set the javascript variable to true, thus hiding the voting.
- If the page happens to be requested, you'll lose the value set on the javascript variable, but now it won't matter because 'onReady' you're hitting your api to check the value of the session variable, which will be true if they have already voted(and their session has not expired).
Now, all cases are covered and your 'voted' variable that is set in javascript will always hold 'true' or 'false', giving you a way to determine whether or not to show an element on screen or whether or not to submit a vote. You could even take it further and update your controller method that processes the vote, to check the session before performing the voting logic.
Admittedly, if someone wanted to bad enough, they could short-circuit this process. There's several more layers of security you could add in, it just depends on how far you care to take it.
At any rate, I hope you get this working like you want it.
Clay
Oh, I'll work on putting a fiddle together.
Thanks @clay,
I'm working on it to at the same time the fiddle would be fantastic.
Please or to participate in this conversation.