I am new to Laravel, so sorry for my code..
I am trying to integrate a jquery library with my Laravel project.
Controller
public function index()
{
return view('products');
}
public function data()
{
$products = Product::all();
return $products->toJson();
}
Route
Route::get('/products', ['as' => 'products', 'uses' => 'ProductController#index']);
Route::get('/products/data', ['as' => 'products.data', 'uses' => 'ProductController#data']);
View
<script>
var CSRF_TOKEN = $('meta[name="csrf-token"]').attr('content');
$.ajax({
url: '/products/data/',
type: 'GET',
data: {_token: CSRF_TOKEN},
dataType: 'JSON',
success: function (data) {
console.log(data);
}
});
</script>
Everything works, but if I go to /products/data/ I can see the json on the browser. That should not happen!
Am I doing it right? It this the right way of getting json data from the database into the view?
Thank you.
As long this information is not usefull for an attacker you've nothing to worry about. Product information is most likely not a thing you want if you want to do some harm to a website.
Make sure information about users doesn't transfer over a GET Request. Because this way someone who wants to do harm to your website has access to information they want to achieve. Make sure this data travels over a POST Request so they can't get access to the information very easy. Also make sure you hash information that should be only in the hands of the user him or herself or other trusted sources.
In this situation i don't really see anything wrong with your approach at first sight.
A little more information about this subject can be found here: HTTP Methods: GET vs. POST
You can use Request wantsJson or ajax method
Controller
use Illuminate\Http\Request;
public function data(Request $request)
{
$products = Product::all();
if ($request->wantsJson()) {
return $products;
}
return abort(404);
}
Related
I am validating a contact form. I wrapped it in a div with the id of "contactform". When validation messages appear after submission I want the redirected page to scroll down automatically to the "contactform" div so users don't have to scroll down themselves to correct the form.
I can accomplish this easily by manually typing the url:localhost:8000/#contactform
However I cannot get the routing to work in Laravel.
I tried altering the controller to:
return Redirect::to('/#contactform');
I tried:
return Redirect::to('#contactform');
return Redirect::to('/' . '#contactform');
return view('/')->with('#contactform');
return view('/#contactform');
return view('/pages.index.#contactform');
return view('/pages.index#contactform');
They all return without the #contactform in the url.
I know there is a javascript way to accomplish this, but I was really trying to stay away from javascript for now. There has to be a simpler way I just cannot see it. I'm new to Laravel and completely self-taught. Thanks
***EDIT
I tried a new controller step by step. Without validation it works. Validation seems to break it.
This works:
public function store() {
return (string) Redirect::to('/#contactform');
}
This breaks it:
public function store(Request $request) {
$this->validate($request, [
'first_name' => 'required',
'last_name' => 'required',
'email' => 'required|email',
'telephone' => 'nullable',
'comments' => 'required',
]);
return (string) Redirect::to('/#contactform');
}
The top of the controller page I call the request and redirect like this:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
Can anyone help me get down to the reason why I cannot validate without breaking the url route?
On validation failure it will cause a redirect 'back'. It is entirely possible that 'back' is the same URI you want to redirect to, so from your perspective it looks like it is redirecting wrong when it is correct. If you remove the validation you should get the correct redirect.
Side note: On the server side the hash is not part of the URL. It is a client side thing.
A solution that works for me is to compact() a Boolean variable through the controller.
Link example
Passing the variable to the controller
Route::get('/post={post_id}+{scroll}',[PostController::class, 'show']);
If the variable equals 'scroll' then switch the boolean variable to true
public function show($post_id, $scroll){
if($scroll == 'scroll'){
$scroll = true;
}else{
$scroll = false;
}
return view('yourview', compact('scroll'))
}
Run the script if $scroll is true
<script>
#if($scroll)
$(document).ready(function () {
$('html,body').animate({ scrollTop: 99999 }, 'slow');
});
#endif
</script>
New to MVC.
Scenario is. Using a 3rd party upload library for images. When a form is submitted, I want to make a call via ajax to submit the data and return the inserted item id. I then use that id for the 3rd party upload library to build folders where the images will be uploaded to.
I have the ajax call working and inserting the data to the database and getting the inserted id. But when the debug returns from the controller, it renders the id as a whole page.
Missing something fundamental here to MVC I think.
cshtml file:
<div class="col-md-8">
<input type="submit" value="Add Item" id="submitItem" />
<script>
$(document).ready(function () {
$("#submitItem").submit(function () {
event.preventDefault();
insertData();
});
});
function insertData()
{
var requestData = {
userID: $("#hdnUserID").val(),
title: $("#title").val(),
typeID: $("#typeID").val(),
description: $("#description").val()
};
$.ajax({
url: '<%= Url.Action("ItemUserDashBoard", "Home") %>',
type: 'post',
data: JSON.stringify(requestData),
dataType: 'json',
success: function (data) {
// your data could be a View or Json or what ever you returned in your action method
// parse your data here
alert(data);
$("#fine-uploader-gallery").fineUploader("uploadStoredFiles");
},
processData: false
});
}
</script>
</div>
HomeController.cs
[HttpPost]
public JsonResult ItemUserDashBoard(ItemAppraise.Models.iaDashBoardModel objItemUserDashBoard)
{
if(ModelState.IsValid)
{
using (dbContext)
{
ia_items iaItem = new ia_items
{
userID = objItemUserDashBoard.iaItems.userID,
typeID = objItemUserDashBoard.iaItems.typeID,
title = objItemUserDashBoard.iaItems.title,
description = objItemUserDashBoard.iaItems.description,
lastUpdate = DateTime.Now
};
dbContext.ia_items.Add(iaItem);
dbContext.SaveChanges();
//objItemUserDashBoard.iaItems.itemID = iaItem.itemID;
return Json(iaItem.itemID.ToString());
}
}
else{
return null;
}
}
Fiddler shows it as having a header of Content-Type: application/json; charset=utf-8.
But the page renders under the control url 'http://localhost:55689/Home/ItemUserDashBoard' with just the item id showing.
How do I get the data back just to use in the success part of the ajax call and not be rendered? Is this Partial Views or something similar?
Any guidance is appreciated.
In standard MVC. Any call made to a controller is handled just like a web request. So if i understand you correctly - the result of your httpPost is being rendered instead of the desired View? This is because you are returning JSON, so the controller assumes that is what you are trying to render. If you want a View to be rendered instead (and somehow use that response data) you could try setting the return type to ActionResult and returning a View("nameofview"); You can pass your response data to that view in a number of ways.
As a side note I think the problem you are facing could be better solved with Web Api instead of MVC. It works well with MVC and could be a simpler way of implementing your desired functionality. Separating your post requests and database interactions from the logic which decides which View to return.
I'm creating a website where I want to perform CRUD operations to a dataset. For the sake of simplicity, assume a table of books. I understand that example.com/books should return a list of books, and example.com/books/24 should return the book with id 24.
Now, imagine that my list of books is very large, and I want to allow the user to browse the book list using a table with pagination, but as the dataset is very large I want to retrieve only the current page using AJAX.
The question is: should example.com/books return the HTML containing the table with all pagination controls and other widgets? or should it return the data in JSON format? What would be the right way to perform both calls.
Thanks in advance
In your controller you could do something like this:
function index (Request $request){
$books = Book::paginate(10);
if($request->ajax()){
//ajax request
return Response::json(view('books', compact('books'))->render());
}
//html request
return view('books', compact('books'));
}
You can pass a page parameter to the route to navigate to pages.
Example : example.com/books?page=2 will fetch the second page of results.
Suggested Approach
Render the initial request with as html like you would normaly do. Then for the next page, get the second page rendered as an ajax call and append it to the DOM.
return Response::json(view('books', compact('books'))->render());
$('.pager a').on('click', function (e) {
var page = $(this).attr('href').split('page=')[1];
e.preventDefault();
$.ajax({
type: "GET",
url: 'page=' + page,
dataType: 'json',
success:function(json){
alert("Success"+json);
},
error:function(){
alert("Error");
}
});
});
});
Take a look at Laravel Pagination
I am needing the scope of $scope.press to fall within the scope of ReleaseCtrl, wondering the best way to refactor the syntax to do so... I know it's a scope inheritance issue because when I try to post the same console.log($scope.press) below the close of the query function, I get an undefined response as opposed to the array, which properly shows up within the bounds of the "Release" query function.
app.service('Releases', function ($resource) {
return $resource('http://127.0.0.1:3000/json', {}, {
'query': {method: 'GET', isArray: true}
});
});
app.controller("ReleaseCtrl", function (Releases, $scope){
Releases.query(function(data, $scope){
$scope.press = data;
console.log($scope.press);
for(var i=0;i<$scope.press.length;i++) {
console.log($scope.press[i].name)
}
});
$scope.loadRelease = function() {
console.log("Loading Press Release");
}
})
I imagined the Releases.query function could find its way to replace the function directly above it, but, Releases of course is undefined at that point because it has not yet been passed / injected.
I think this is fairly simple to fix, but I do not know exactly the best way to go about it.
Best regards,
Sean
Here is what I ended up doing:
app.service('Releases', function ($resource) {
return $resource('http://127.0.0.1:3000/json', {}, {
'query': {method: 'GET', isArray: true}
});
});
app.factory ('release', function (Releases){
return Releases.query();
})
app.controller("ReleaseCtrl", function (release, $scope){
$scope.press = release;
console.log($scope.press);
$scope.loadRelease = function() {
console.log("Loading Press Release");
}
})
After looking around, I believed injecting with service was better practice than what I was doing earlier, so I just refactored it that way. I think re-writing what is in the service within the factory is even better. Will update when I get there.
A thing to note: Whatever you query must have more than one result. Otherwise it comes back as an object, not an array, and it does not work correctly. Maybe somebody can explain why?
Since you are passing $scope as a parameter to the function you pass to .query, and because .query does not call the callback with any arguments, $scope is probably undefined.
You need to use the following syntax for accessing your resources:
var data = Releases.query(function () {
$scope.press = data;
// ... Further operations with data.
});
See usage note in the docs
I am trying to convert a regular old controller I was using to an API controller and am having a little bit of difficulty. What these series of functions do is, in the jQuery, it iterates over a file containing all the usernames of employees and for each username it makes a call to the PopulateEmployee method in my webapi controller which should return JSON and then populate a results div.
When manually navigating to
..domain../staffinformation/populateemployee/employeeusername
I get the error
This XML file does not appear to have any style information associated with it. The
document tree is shown below.
<Error>
<Message>
The requested resource does not support http method 'GET'.
</Message>
</Error>
Please note that the div it will be populating is a partial view in an Umbraco CMS page and I don't think that is the problem but if you guys think differently please tell me.
There has to be something I am missing either with webAPI routing or something else.
Thanks for your help.
Here's the codez.
Please notice that this method has the HttpPost tag
public class StaffInformationController : ApiController
{
[System.Web.Http.ActionName("PopulateEmployee")]
[System.Web.Http.HttpPost]
public StaffListing PopulateEmployee(string id)
{
//do error checking on input
StaffListing staffListing = new StaffListing(id);
//populate other fields
return staffListing;
}
}
The routing set up for the api controller
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
The jQuery call specifying use of 'POST', please forgive the trickiness of the recursive call in this function.
function getEmployeeObjectByIndex() {
$.ajax({
url: $('#root').val() + '/api/StaffInformation/PopulateEmployee',
type: 'POST',
async: true,
contentType: 'application/json, charset=utf-8',
data: JSON.stringify({ 'username': lines[i] }),
success: function (staffObject) {
if (!(staffObject.Name == undefined)) {
buildHtmlStrings(staffObject);
}
i++;
getEmployeeObjectByIndex(); //recursive call
}
});
}
manually navigating to that address throws the error because, when manually navigating you are doing a GET (and your method only allows POSTs).
You should fire up Fiddler and watch the ajax POST request and response to see how the server is responding / your request is being made
Jquery ------> web api
Web API has one property i.e. CONTENT NEGOTIATION means you send any data and accept any data as you want.
$.ajax({
contentType: 'application/json, charset=utf-8',
// this is sending your data of datatype json to server, here you send any type of data
accept: 'application/json',
//this is receiving/getting data form server to client...
// SO HERE YOU GET JSON DATA AS YOU WANT only mention which data of datatype u want...
//if you sending xml and you want json so only write accept as json it get automatically converted into your required datatype..by MediaTypeFormatter
});